1. Data Set Description For this project, our team decided to take a data-driven approach to evaluate music. We are using the Spotify Dataset from 1921 to 2020 that consists of over 160,000 different tracks. In order to stay consistent in the evaluation, all data was sourced from the Spotify Web API. The following set of data is combination of Primary, Numerical, Dummy(binary), and Categorical data. This allows us to explore different types of models and draw different insights. Here are all the variables that is included in the data set:

Primary - id: this an unique key comprised of numbers and characters that is assigned to each track generated by Spotify

Numerical - acousticness: range from 0(LOW) to 1(HIGH) - danceability: range from 0(LOW) to 1(HIGH) - energy: range from 0(LOW) to 1(HIGH) - duration_ms: majority range from 200,000 to 300,000 - instrumentalness: range from 0(LOW) to 1(HIGH) - valence: range from 0(LOW) to 1(HIGH) - popularity: range from 0(LOW) to 100(HIGH)* - tempo: majority range from 50(LOW) to 150(high) - liveness: range from 0(LOW) to 1(HIGH) - loudness majority range from -60 to 0 - speechiness: range from 0(LOW) to 1(HIGH) - year: range from 1921 to 2020

Dummy - mode: 0 represents minor and 1 represents major - explicit: 0 represents no explicit content and 1 represents explicit content

Categorical - key: this consists of all different music keys on onctave encoded from 0 to 11 (i.e. C = 0, C# = 1, etc…) - artists: the artist of the track - release_date: the date of release in yyyy-mm-dd format - name: the name of the track

*With our approach on evaluating different tracks, we decided to use popularity as our main dependant variable.

options(repr.matrix.max.rows=100, repr.matrix.max.cols=20)

Import all Libraries

# Libraries
options(warn=-1)
library(ggplot2)
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(plotly)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
library(hrbrthemes)
NOTE: Either Arial Narrow or Roboto Condensed fonts are required to use these themes.
      Please use hrbrthemes::import_roboto_condensed() to install Roboto Condensed and
      if Arial Narrow is not on your system, please see https://bit.ly/arialnarrow
library(forecast)
Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
library(xts)
Loading required package: zoo

Attaching package: ‘zoo’

The following objects are masked from ‘package:base’:

    as.Date, as.Date.numeric


Attaching package: ‘xts’

The following objects are masked from ‘package:dplyr’:

    first, last
library(Metrics)

Attaching package: ‘Metrics’

The following object is masked from ‘package:forecast’:

    accuracy
library(psych)

Attaching package: ‘psych’

The following objects are masked from ‘package:ggplot2’:

    %+%, alpha
library(dygraphs)
library(GGally)
Registered S3 method overwritten by 'GGally':
  method from   
  +.gg   ggplot2
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ tibble  3.0.3     ✓ purrr   0.3.4
✓ tidyr   1.1.2     ✓ stringr 1.4.0
✓ readr   1.4.0     ✓ forcats 0.5.0
── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x psych::%+%()     masks ggplot2::%+%()
x psych::alpha()   masks ggplot2::alpha()
x plotly::filter() masks dplyr::filter(), stats::filter()
x xts::first()     masks dplyr::first()
x dplyr::lag()     masks stats::lag()
x xts::last()      masks dplyr::last()
library(tidyquant)  
Loading required package: lubridate

Attaching package: ‘lubridate’

The following objects are masked from ‘package:base’:

    date, intersect, setdiff, union

Loading required package: PerformanceAnalytics

Attaching package: ‘PerformanceAnalytics’

The following object is masked from ‘package:graphics’:

    legend

Loading required package: quantmod
Loading required package: TTR
Version 0.4-0 included new data defaults. See ?getSymbols.
══ Need to Learn tidyquant? ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════
Business Science offers a 1-hour course - Learning Lab #9: Performance Analysis & Portfolio Optimization with tidyquant!
</> Learn more at: https://university.business-science.io/p/learning-labs-pro </>
library(cranlogs)   
library(corrr)      
library(cowplot)  

Attaching package: ‘cowplot’

The following object is masked from ‘package:lubridate’:

    stamp
library(Metrics)

Initial data visualization

Plotting annual median values for each feature overall. Standardized such that each feature is on the same scale.

df <- read.csv("./data/data.csv")
q1s = data.frame(matrix(ncol=3, nrow=0))
medians = data.frame(matrix(ncol=3, nrow=0))
q3s = data.frame(matrix(ncol=3, nrow=0))
feature_names = sort(c("acousticness", "danceability", "instrumentalness", "energy",
                  "duration_ms", "valence", "tempo", "liveness", "loudness", "speechiness"))
for(i in 1921:2020){
  annual_val = subset(df, year==i)

  for(name in feature_names){
    vals = quantile(annual_val[,name])[2:4]
    q1s <- rbind(q1s, c(i, name, vals[1]))
    medians <- rbind(medians, c(i, name, vals[2]))
    q3s <- rbind(q3s, c(i, name, vals[3]))
  }
}

colnames(q1s) <-c("Year", "Feature", "Value")
colnames(medians) <-c("Year", "Feature", "Value")
colnames(q3s) <-c("Year", "Feature", "Value")

split_q1s <- split(q1s, q1s$Feature)
split_medians <- split(medians, medians$Feature)
split_q3s <- split(q3s, q3s$Feature)

feature_quantiles = array(0, c(100,3,length(feature_names)))

for(i in 1:length(feature_names)){
  for(j in 1:100){
    feature_quantiles[j,1,i] = split_q1s[[i]][[3]][j]
    feature_quantiles[j,2,i] = split_medians[[i]][[3]][j]
    feature_quantiles[j,3,i] = split_q3s[[i]][[3]][j]
  }
}

dimnames(feature_quantiles) <- list(1921:2020,
                                    c("Q1", "Median", "Q3"),
                                    feature_names)



plot(1930,1, xlim = c(1921,2020), ylim = range(0,15), xlab="Year", ylab="Range")
for(i in 1:length(feature_names)){
  name = feature_names[i]
  medians = as.numeric(feature_quantiles[,,name][,"Median"])

  avg = mean(medians)
  medians = medians / avg
  
  lines(x=1921:2020, y=medians, col=i)
}
legend("topright", lty=1, col=c(1,2,3,4,5,6,7,8,9,10), legend=feature_names)

Plotting annual median values for the feature for the top 10% of songs by popularity. Standardized such that each feature is on the same scale.

q1s = data.frame(matrix(ncol=3, nrow=0))
medians = data.frame(matrix(ncol=3, nrow=0))
q3s = data.frame(matrix(ncol=3, nrow=0))
feature_names = sort(c("acousticness", "danceability", "instrumentalness", "energy",
                  "duration_ms", "valence", "tempo", "liveness", "loudness", "speechiness"))
for(i in 1921:2020){
  annual_val = subset(df, year==i)
  
  
  top_10 = floor(nrow(annual_val)*0.1)
  annual_val = annual_val[order(annual_val$popularity, decreasing=TRUE),]
  annual_val = annual_val[1:top_10,]

  for(name in feature_names){
    vals = quantile(annual_val[,name])[2:4]
    q1s <- rbind(q1s, c(i, name, vals[1]))
    medians <- rbind(medians, c(i, name, vals[2]))
    q3s <- rbind(q3s, c(i, name, vals[3]))
  }
}

colnames(q1s) <-c("Year", "Feature", "Value")
colnames(medians) <-c("Year", "Feature", "Value")
colnames(q3s) <-c("Year", "Feature", "Value")

split_q1s <- split(q1s, q1s$Feature)
split_medians <- split(medians, medians$Feature)
split_q3s <- split(q3s, q3s$Feature)

feature_quantiles = array(0, c(100,3,length(feature_names)))

for(i in 1:length(feature_names)){
  for(j in 1:100){
    feature_quantiles[j,1,i] = split_q1s[[i]][[3]][j]
    feature_quantiles[j,2,i] = split_medians[[i]][[3]][j]
    feature_quantiles[j,3,i] = split_q3s[[i]][[3]][j]
  }
}

dimnames(feature_quantiles) <- list(1921:2020,
                                    c("Q1", "Median", "Q3"),
                                    feature_names)


plot(1930,1, xlim = c(1921,2020), ylim = range(0,17), xlab="Year", ylab="Range")
for(i in 1:length(feature_names)){
  name = feature_names[i]
  medians = as.numeric(feature_quantiles[,,name][,"Median"])
  avg = mean(medians)
  medians = medians / avg
  
  lines(x=1921:2020, y=medians, col=i)
}
legend("topright", lty=1, col=c(1,2,3,4,5,6,7,8,9,10), legend=feature_names)

####Insights:
Both these graphs appear to have similar shapes, with instrumentalness being the most widely varying, and the rest varying marginally. However, for the speechiness variable,

Train time series models to forecast the future models.

# Set up the libraries and the training/testing amounts
library("smooth")
Loading required package: greybox
Package "greybox", v0.6.3 loaded.


Attaching package: ‘greybox’

The following object is masked from ‘package:lubridate’:

    hm

The following object is masked from ‘package:tidyr’:

    spread

This is package "smooth", v2.6.0

Attaching package: ‘smooth’

The following object is masked from ‘package:TTR’:

    lags
library("forecast")
library("nnfor")
training.percent = 0.95
nTrain = 100*training.percent
nTest = 100*(1-training.percent)

Accousticness Models

acousticness = ts(as.numeric(feature_quantiles[,"acousticness"]) , start=1921)
acousticness.train = subset(acousticness, start=1, end=nTrain)
acousticness.test = subset(acousticness, start = (nTrain+1), end =(nTrain+nTest))

#ses
acousticness.train.ses <- ses(acousticness.train, h=nTest)
acousticness.ses.mape = mape(acousticness.train.ses$mean, acousticness.test)
plot(acousticness.train.ses, main=paste("Acousticness", "SES"), sub=paste("MAPE:", round(acousticness.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(acousticness)


#SARIMA
acousticness.sarima.model <- auto.arima(acousticness.train)
acousticness.sarima <- forecast(acousticness.sarima.model, h=nTest)
acousticness.sarima.mape = mape(acousticness.sarima$mean, acousticness.test)
plot(acousticness.sarima, main=paste("Acousticness", "SARIMA"), sub=paste("MAPE:", round(acousticness.sarima.mape*100,3), "%"), xlab="Year", ylab="Median Value")
lines(acousticness)


#Neural Network
acousticness.train.nn <- elm(acousticness.train)
acousticness.nn.forecast <- forecast(acousticness.train.nn, h=nTest)
acousticness.nn.mape = mape(acousticness.nn.forecast$mean, acousticness.test)
plot(acousticness, main=paste("Acousticness", "NN"), sub=paste("MAPE:", round(acousticness.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(acousticness.nn.forecast$mean, start=1921+nTrain), col=2)

Here we see that the best method to predict future acousticness median values for the overall data set is through SES. However, given that this best MAPE is roughly 37%, this feature cannot be reliably forecast into the future.

Energy models

energy = ts(as.numeric(feature_quantiles[,"energy"]) , start=1921)
energy.train = subset(energy, start=1, end=nTrain)
energy.test = subset(energy, start = (nTrain+1), end =(nTrain+nTest))

#SES
energy.train.ses <- ses(energy.train, h=nTest)
energy.ses.mape = mape(energy.train.ses$mean, energy.test)
plot(energy.train.ses, main=paste("Energy", "SES"), sub=paste("MAPE:", round(energy.ses.mape* 100, 3), "%"), xlab="Year", ylab="Median Value")
lines(energy)


#SARIMA
energy.sarima.model <- auto.arima(energy.train)
energy.sarima <- forecast(energy.sarima.model, h=nTest)
energy.sarima.mape = mape(energy.sarima$mean, energy.test)
plot(energy.sarima, main=paste("Energy", "SARIMA"), sub=paste("MAPE:", round(energy.sarima.mape* 100, 3), "%"), xlab="Year", ylab="Median Value")
lines(energy)


#Neural Network
energy.train.nn <- elm(energy.train)
energy.nn.forecast <- forecast(energy.train.nn, h=nTest)
energy.nn.mape = mape(energy.nn.forecast$mean, energy.test)
plot(energy, main=paste("Energy", "NN"), sub=paste("MAPE:", round(energy.nn.mape* 100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(energy.nn.forecast$mean, start=1921+nTrain), col=2)

Here we see that the best method to predict future energy median values for the overall data set is through SES. Given that this best MAPE is roughly 8%, this feature can be fairly reliably forecast into the future.

Danceability models

danceability = ts(as.numeric(feature_quantiles[,"danceability"]) , start=1921)
danceability.train = subset(danceability, start=1, end=nTrain)
danceability.test = subset(danceability, start = (nTrain+1), end =(nTrain+nTest))

#SES
danceability.train.ses <- ses(danceability.train, h=nTest)
danceability.ses.mape = mape(danceability.train.ses$mean, danceability.test)
plot(danceability.train.ses, main=paste("danceability", "SES"), sub=paste("MAPE:", round(danceability.ses.mape* 100, 3), "%"), xlab="Year", ylab="Median Value")
lines(danceability)


#SARIMA
danceability.sarima.model <- auto.arima(danceability.train)
danceability.sarima <- forecast(danceability.sarima.model, h=nTest)
danceability.sarima.mape = mape(danceability.sarima$mean, danceability.test)
plot(danceability.sarima, main=paste("danceability", "SARIMA"), sub=paste("MAPE:", round(danceability.sarima.mape* 100, 3), "%"), xlab="Year", ylab="Median Value")
lines(danceability)


#Neural Network
danceability.train.nn <- elm(danceability.train)
danceability.nn.forecast <- forecast(danceability.train.nn, h=nTest)
danceability.nn.mape = mape(danceability.nn.forecast$mean, danceability.test)
plot(danceability, main=paste("danceability", "NN"), sub=paste("MAPE:", round(danceability.nn.mape* 100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(danceability.nn.forecast$mean, start=1921+nTrain), col=2)

Here we see that the best method to predict future danceability median values for the overall data set is through NN. However, given that this best MAPE is roughly 10.4%, this feature cannot be reliably forecast into the future.

Duration models

duration = ts(as.numeric(feature_quantiles[,"duration_ms"]) , start=1921)
duration.train = subset(duration, start=1, end=nTrain)
duration.test = subset(duration, start = (nTrain+1), end =(nTrain+nTest))

#SES
duration.train.ses <- ses(duration.train, h=nTest)
duration.ses.mape = mape(duration.train.ses$mean, duration.test)
plot(duration.train.ses, main=paste("Duration", "SES"), sub=paste("MAPE:",round(duration.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(duration)


#SARIMA
duration.sarima.model <- auto.arima(duration.train)
duration.sarima <- forecast(duration.sarima.model, h=nTest)
duration.sarima.mape = mape(duration.sarima$mean, duration.test)
plot(duration.sarima, main=paste("Duration", "SARIMA"), sub=paste("MAPE:", round(duration.sarima.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(duration)



#Neural Network
duration.train.nn <- elm(duration.train)
duration.nn.forecast <- forecast(duration.train.nn, h=nTest)
duration.nn.mape = mape(duration.nn.forecast$mean, duration.test)
plot(duration, main=paste("Duration", "NN"), sub=paste("MAPE:", round(duration.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(duration.nn.forecast$mean, start=1921+nTrain), col=2)

Here we see that the best method to predict future duration median values for the overall data set is through NN. Given that this best MAPE is roughly 8%, this feature can be fairly reliably forecast into the future.

Valence

valence = ts(as.numeric(feature_quantiles[,"valence"]) , start=1921)
valence.train = subset(valence, start=1, end=nTrain)
valence.test = subset(valence, start = (nTrain+1), end =(nTrain+nTest))

#SES
valence.train.ses <- ses(valence.train, h=nTest)
valence.ses.mape = mape(valence.train.ses$mean, valence.test)
plot(valence.train.ses, main=paste("valence", "SES"), sub=paste("MAPE:",round(valence.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(valence)


#SARIMA
valence.sarima.model <- auto.arima(valence.train)
valence.sarima <- forecast(valence.sarima.model, h=nTest)
valence.sarima.mape = mape(valence.sarima$mean, valence.test)
plot(valence.sarima, main=paste("valence", "SARIMA"), sub=paste("MAPE:", round(valence.sarima.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(valence)



#Neural Network
valence.train.nn <- elm(valence.train)
valence.nn.forecast <- forecast(valence.train.nn, h=nTest)
valence.nn.mape = mape(valence.nn.forecast$mean, valence.test)
plot(valence, main=paste("valence", "NN"), sub=paste("MAPE:", round(valence.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(valence.nn.forecast$mean, start=1921+nTrain), col=2)

Insights:

Relatively lower MAPE indicates that we can predict valence over time. The best model to use is the SARIMA model because it has the lowest MAPE at 6.7%.

Tempo model

tempo = ts(as.numeric(feature_quantiles[,"tempo"]) , start=1921)
tempo.train = subset(tempo, start=1, end=nTrain)
tempo.test = subset(tempo, start = (nTrain+1), end =(nTrain+nTest))

#SES
tempo.train.ses <- ses(tempo.train, h=nTest)
tempo.ses.mape = mape(tempo.train.ses$mean, tempo.test)
plot(tempo.train.ses, main=paste("tempo", "SES"), sub=paste("MAPE:",round(tempo.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(tempo)


#SARIMA
tempo.sarima.model <- auto.arima(tempo.train)
tempo.sarima <- forecast(tempo.sarima.model, h=nTest)
tempo.sarima.mape = mape(tempo.sarima$mean, tempo.test)
plot(tempo.sarima, main=paste("tempo", "SARIMA"), sub=paste("MAPE:", round(tempo.sarima.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(tempo)



#Neural Network
tempo.train.nn <- elm(tempo.train)
tempo.nn.forecast <- forecast(tempo.train.nn, h=nTest)
tempo.nn.mape = mape(tempo.nn.forecast$mean, tempo.test)
plot(tempo, main=paste("tempo", "NN"), sub=paste("MAPE:", round(tempo.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(tempo.nn.forecast$mean, start=1921+nTrain), col=2)

Insights:

Here we see that the best method to predict future tempo median values for the overall data set is through SES. Given that this best MAPE is roughly 1%, this feature can be fairly reliably forecast into the future.

Liveness

liveness = ts(as.numeric(feature_quantiles[,"liveness"]) , start=1921)
liveness.train = subset(liveness, start=1, end=nTrain)
liveness.test = subset(liveness, start = (nTrain+1), end =(nTrain+nTest))

#SES
liveness.train.ses <- ses(liveness.train, h=nTest)
liveness.ses.mape = mape(liveness.train.ses$mean, liveness.test)
plot(liveness.train.ses, main=paste("liveness", "SES"), sub=paste("MAPE:",round(liveness.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(liveness)


#SARIMA
liveness.sarima.model <- auto.arima(liveness.train)
liveness.sarima <- forecast(liveness.sarima.model, h=nTest)
liveness.sarima.mape = mape(liveness.sarima$mean, liveness.test)
plot(liveness.sarima, main=paste("liveness", "SARIMA"), sub=paste("MAPE:", round(liveness.sarima.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(liveness)



#Neural Network
liveness.train.nn <- elm(liveness.train)
liveness.nn.forecast <- forecast(liveness.train.nn, h=nTest)
liveness.nn.mape = mape(liveness.nn.forecast$mean, liveness.test)
plot(liveness, main=paste("liveness", "NN"), sub=paste("MAPE:", round(liveness.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(liveness.nn.forecast$mean, start=1921+nTrain), col=2)

Insights:

Here we see that the best method to predict future liveness median values for the overall data set is through SARIMA Given that this best MAPE is roughly 2.6%, this feature can be fairly reliably forecast into the future.

Loudness

loudness = ts(as.numeric(feature_quantiles[,"loudness"]) , start=1921)
loudness.train = subset(loudness, start=1, end=nTrain)
loudness.test = subset(loudness, start = (nTrain+1), end =(nTrain+nTest))

#SES
loudness.train.ses <- ses(loudness.train, h=nTest)
loudness.ses.mape = mape(loudness.train.ses$mean, loudness.test)
plot(loudness.train.ses, main=paste("loudness", "SES"), sub=paste("MAPE:",round(loudness.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(loudness)


#SARIMA
loudness.sarima.model <- auto.arima(loudness.train)
loudness.sarima <- forecast(loudness.sarima.model, h=nTest)
loudness.sarima.mape = mape(loudness.sarima$mean, loudness.test)
plot(loudness.sarima, main=paste("loudness", "SARIMA"), sub=paste("MAPE:", round(loudness.sarima.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(loudness)



#Neural Network
loudness.train.nn <- elm(loudness.train)
loudness.nn.forecast <- forecast(loudness.train.nn, h=nTest)
loudness.nn.mape = mape(loudness.nn.forecast$mean, loudness.test)
plot(loudness, main=paste("loudness", "NN"), sub=paste("MAPE:", round(loudness.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(loudness.nn.forecast$mean, start=1921+nTrain), col=2)

Insights:

Here we see that the best method to predict future loudness median values for the overall data set is through SES Given that this best MAPE is roughly 5%, this feature can be fairly reliably forecast into the future.

LS0tCnRpdGxlOiAiU3BvdGlmeSBEYXRhIEFuYWx5c2lzIGFuZCBEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYXV0aG9yOiBDUyBEdWFscwpkYXRlOiBOb3ZlbWJlciAxOCwgMjAyMAotLS0KCjEuIERhdGEgU2V0IERlc2NyaXB0aW9uCkZvciB0aGlzIHByb2plY3QsIG91ciB0ZWFtIGRlY2lkZWQgdG8gdGFrZSBhIGRhdGEtZHJpdmVuIGFwcHJvYWNoIHRvIGV2YWx1YXRlIG11c2ljLiBXZSBhcmUgdXNpbmcgdGhlIFNwb3RpZnkgRGF0YXNldCBmcm9tIDE5MjEgdG8gMjAyMCB0aGF0IGNvbnNpc3RzIG9mIG92ZXIgMTYwLDAwMCBkaWZmZXJlbnQgdHJhY2tzLiBJbiBvcmRlciB0byBzdGF5IGNvbnNpc3RlbnQgaW4gdGhlIGV2YWx1YXRpb24sIGFsbCBkYXRhIHdhcyBzb3VyY2VkIGZyb20gdGhlIFNwb3RpZnkgV2ViIEFQSS4gVGhlIGZvbGxvd2luZyBzZXQgb2YgZGF0YSBpcyBjb21iaW5hdGlvbiBvZiBQcmltYXJ5LCBOdW1lcmljYWwsIER1bW15KGJpbmFyeSksIGFuZCBDYXRlZ29yaWNhbCBkYXRhLiBUaGlzIGFsbG93cyB1cyB0byBleHBsb3JlIGRpZmZlcmVudCB0eXBlcyBvZiBtb2RlbHMgYW5kIGRyYXcgZGlmZmVyZW50IGluc2lnaHRzLiBIZXJlIGFyZSBhbGwgdGhlIHZhcmlhYmxlcyB0aGF0IGlzIGluY2x1ZGVkIGluIHRoZSBkYXRhIHNldDoKClByaW1hcnkKICAgIC0gaWQ6IHRoaXMgYW4gdW5pcXVlIGtleSBjb21wcmlzZWQgb2YgbnVtYmVycyBhbmQgY2hhcmFjdGVycyB0aGF0IGlzIGFzc2lnbmVkIHRvIGVhY2ggdHJhY2sgZ2VuZXJhdGVkIGJ5IFNwb3RpZnkKCk51bWVyaWNhbAogICAgLSBhY291c3RpY25lc3M6IHJhbmdlIGZyb20gMChMT1cpIHRvIDEoSElHSCkKICAgIC0gZGFuY2VhYmlsaXR5OiByYW5nZSBmcm9tIDAoTE9XKSB0byAxKEhJR0gpCiAgICAtIGVuZXJneTogcmFuZ2UgZnJvbSAwKExPVykgdG8gMShISUdIKQogICAgLSBkdXJhdGlvbl9tczogbWFqb3JpdHkgcmFuZ2UgZnJvbSAyMDAsMDAwIHRvIDMwMCwwMDAKICAgIC0gaW5zdHJ1bWVudGFsbmVzczogcmFuZ2UgZnJvbSAwKExPVykgdG8gMShISUdIKQogICAgLSB2YWxlbmNlOiByYW5nZSBmcm9tIDAoTE9XKSB0byAxKEhJR0gpCiAgICAtIHBvcHVsYXJpdHk6IHJhbmdlIGZyb20gMChMT1cpIHRvIDEwMChISUdIKSoKICAgIC0gdGVtcG86IG1ham9yaXR5IHJhbmdlIGZyb20gNTAoTE9XKSB0byAxNTAoaGlnaCkKICAgIC0gbGl2ZW5lc3M6IHJhbmdlIGZyb20gMChMT1cpIHRvIDEoSElHSCkKICAgIC0gbG91ZG5lc3MgbWFqb3JpdHkgcmFuZ2UgZnJvbSAtNjAgdG8gMAogICAgLSBzcGVlY2hpbmVzczogcmFuZ2UgZnJvbSAwKExPVykgdG8gMShISUdIKQogICAgLSB5ZWFyOiByYW5nZSBmcm9tIDE5MjEgdG8gMjAyMAoKRHVtbXkKICAgIC0gbW9kZTogMCByZXByZXNlbnRzIG1pbm9yIGFuZCAxIHJlcHJlc2VudHMgbWFqb3IKICAgIC0gZXhwbGljaXQ6IDAgcmVwcmVzZW50cyBubyBleHBsaWNpdCBjb250ZW50IGFuZCAxIHJlcHJlc2VudHMgZXhwbGljaXQgY29udGVudAogICAgCkNhdGVnb3JpY2FsCiAgICAtIGtleTogdGhpcyBjb25zaXN0cyBvZiBhbGwgZGlmZmVyZW50IG11c2ljIGtleXMgb24gb25jdGF2ZSBlbmNvZGVkIGZyb20gMCB0byAxMSAoaS5lLiBDID0gMCwgQyMgPSAxLCBldGMuLi4pCiAgICAtIGFydGlzdHM6IHRoZSBhcnRpc3Qgb2YgdGhlIHRyYWNrCiAgICAtIHJlbGVhc2VfZGF0ZTogdGhlIGRhdGUgb2YgcmVsZWFzZSBpbiB5eXl5LW1tLWRkIGZvcm1hdAogICAgLSBuYW1lOiB0aGUgbmFtZSBvZiB0aGUgdHJhY2sKCipXaXRoIG91ciBhcHByb2FjaCBvbiBldmFsdWF0aW5nIGRpZmZlcmVudCB0cmFja3MsIHdlIGRlY2lkZWQgdG8gdXNlIHBvcHVsYXJpdHkgYXMgb3VyIG1haW4gZGVwZW5kYW50IHZhcmlhYmxlLiAKCgpgYGB7cn0Kb3B0aW9ucyhyZXByLm1hdHJpeC5tYXgucm93cz0xMDAsIHJlcHIubWF0cml4Lm1heC5jb2xzPTIwKQpgYGAKCgpJbXBvcnQgYWxsIExpYnJhcmllcwpgYGB7cn0KIyBMaWJyYXJpZXMKb3B0aW9ucyh3YXJuPS0xKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGhyYnJ0aGVtZXMpCmxpYnJhcnkoZm9yZWNhc3QpCmxpYnJhcnkoeHRzKQpsaWJyYXJ5KE1ldHJpY3MpCmxpYnJhcnkocHN5Y2gpCmxpYnJhcnkoZHlncmFwaHMpCmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWR5cXVhbnQpICAKbGlicmFyeShjcmFubG9ncykgICAKbGlicmFyeShjb3JycikgICAgICAKbGlicmFyeShjb3dwbG90KSAgCmxpYnJhcnkoTWV0cmljcykKYGBgCiMgSW5pdGlhbCBkYXRhIHZpc3VhbGl6YXRpb24KIyMgUGxvdHRpbmcgYW5udWFsIG1lZGlhbiB2YWx1ZXMgZm9yIGVhY2ggZmVhdHVyZSBvdmVyYWxsLiBTdGFuZGFyZGl6ZWQgc3VjaCB0aGF0IGVhY2ggZmVhdHVyZSBpcyBvbiB0aGUgc2FtZSBzY2FsZS4KYGBge3J9CmRmIDwtIHJlYWQuY3N2KCIuL2RhdGEvZGF0YS5jc3YiKQpxMXMgPSBkYXRhLmZyYW1lKG1hdHJpeChuY29sPTMsIG5yb3c9MCkpCm1lZGlhbnMgPSBkYXRhLmZyYW1lKG1hdHJpeChuY29sPTMsIG5yb3c9MCkpCnEzcyA9IGRhdGEuZnJhbWUobWF0cml4KG5jb2w9MywgbnJvdz0wKSkKZmVhdHVyZV9uYW1lcyA9IHNvcnQoYygiYWNvdXN0aWNuZXNzIiwgImRhbmNlYWJpbGl0eSIsICJpbnN0cnVtZW50YWxuZXNzIiwgImVuZXJneSIsCiAgICAgICAgICAgICAgICAgICJkdXJhdGlvbl9tcyIsICJ2YWxlbmNlIiwgInRlbXBvIiwgImxpdmVuZXNzIiwgImxvdWRuZXNzIiwgInNwZWVjaGluZXNzIikpCmZvcihpIGluIDE5MjE6MjAyMCl7CiAgYW5udWFsX3ZhbCA9IHN1YnNldChkZiwgeWVhcj09aSkKCiAgZm9yKG5hbWUgaW4gZmVhdHVyZV9uYW1lcyl7CiAgICB2YWxzID0gcXVhbnRpbGUoYW5udWFsX3ZhbFssbmFtZV0pWzI6NF0KICAgIHExcyA8LSByYmluZChxMXMsIGMoaSwgbmFtZSwgdmFsc1sxXSkpCiAgICBtZWRpYW5zIDwtIHJiaW5kKG1lZGlhbnMsIGMoaSwgbmFtZSwgdmFsc1syXSkpCiAgICBxM3MgPC0gcmJpbmQocTNzLCBjKGksIG5hbWUsIHZhbHNbM10pKQogIH0KfQoKY29sbmFtZXMocTFzKSA8LWMoIlllYXIiLCAiRmVhdHVyZSIsICJWYWx1ZSIpCmNvbG5hbWVzKG1lZGlhbnMpIDwtYygiWWVhciIsICJGZWF0dXJlIiwgIlZhbHVlIikKY29sbmFtZXMocTNzKSA8LWMoIlllYXIiLCAiRmVhdHVyZSIsICJWYWx1ZSIpCgpzcGxpdF9xMXMgPC0gc3BsaXQocTFzLCBxMXMkRmVhdHVyZSkKc3BsaXRfbWVkaWFucyA8LSBzcGxpdChtZWRpYW5zLCBtZWRpYW5zJEZlYXR1cmUpCnNwbGl0X3EzcyA8LSBzcGxpdChxM3MsIHEzcyRGZWF0dXJlKQoKZmVhdHVyZV9xdWFudGlsZXMgPSBhcnJheSgwLCBjKDEwMCwzLGxlbmd0aChmZWF0dXJlX25hbWVzKSkpCgpmb3IoaSBpbiAxOmxlbmd0aChmZWF0dXJlX25hbWVzKSl7CiAgZm9yKGogaW4gMToxMDApewogICAgZmVhdHVyZV9xdWFudGlsZXNbaiwxLGldID0gc3BsaXRfcTFzW1tpXV1bWzNdXVtqXQogICAgZmVhdHVyZV9xdWFudGlsZXNbaiwyLGldID0gc3BsaXRfbWVkaWFuc1tbaV1dW1szXV1bal0KICAgIGZlYXR1cmVfcXVhbnRpbGVzW2osMyxpXSA9IHNwbGl0X3Ezc1tbaV1dW1szXV1bal0KICB9Cn0KCmRpbW5hbWVzKGZlYXR1cmVfcXVhbnRpbGVzKSA8LSBsaXN0KDE5MjE6MjAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiUTEiLCAiTWVkaWFuIiwgIlEzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVfbmFtZXMpCgoKCnBsb3QoMTkzMCwxLCB4bGltID0gYygxOTIxLDIwMjApLCB5bGltID0gcmFuZ2UoMCwxNSksIHhsYWI9IlllYXIiLCB5bGFiPSJSYW5nZSIpCmZvcihpIGluIDE6bGVuZ3RoKGZlYXR1cmVfbmFtZXMpKXsKICBuYW1lID0gZmVhdHVyZV9uYW1lc1tpXQogIG1lZGlhbnMgPSBhcy5udW1lcmljKGZlYXR1cmVfcXVhbnRpbGVzWywsbmFtZV1bLCJNZWRpYW4iXSkKCiAgYXZnID0gbWVhbihtZWRpYW5zKQogIG1lZGlhbnMgPSBtZWRpYW5zIC8gYXZnCiAgCiAgbGluZXMoeD0xOTIxOjIwMjAsIHk9bWVkaWFucywgY29sPWkpCn0KbGVnZW5kKCJ0b3ByaWdodCIsIGx0eT0xLCBjb2w9YygxLDIsMyw0LDUsNiw3LDgsOSwxMCksIGxlZ2VuZD1mZWF0dXJlX25hbWVzKQpgYGAKCgojIyBQbG90dGluZyBhbm51YWwgbWVkaWFuIHZhbHVlcyBmb3IgdGhlIGZlYXR1cmUgZm9yIHRoZSAqdG9wIDEwJSBvZiBzb25ncyogYnkgcG9wdWxhcml0eS4gU3RhbmRhcmRpemVkIHN1Y2ggdGhhdCBlYWNoIGZlYXR1cmUgaXMgb24gdGhlIHNhbWUgc2NhbGUuCmBgYHtyfQpxMXMgPSBkYXRhLmZyYW1lKG1hdHJpeChuY29sPTMsIG5yb3c9MCkpCm1lZGlhbnMgPSBkYXRhLmZyYW1lKG1hdHJpeChuY29sPTMsIG5yb3c9MCkpCnEzcyA9IGRhdGEuZnJhbWUobWF0cml4KG5jb2w9MywgbnJvdz0wKSkKZmVhdHVyZV9uYW1lcyA9IHNvcnQoYygiYWNvdXN0aWNuZXNzIiwgImRhbmNlYWJpbGl0eSIsICJpbnN0cnVtZW50YWxuZXNzIiwgImVuZXJneSIsCiAgICAgICAgICAgICAgICAgICJkdXJhdGlvbl9tcyIsICJ2YWxlbmNlIiwgInRlbXBvIiwgImxpdmVuZXNzIiwgImxvdWRuZXNzIiwgInNwZWVjaGluZXNzIikpCmZvcihpIGluIDE5MjE6MjAyMCl7CiAgYW5udWFsX3ZhbCA9IHN1YnNldChkZiwgeWVhcj09aSkKICAKICAKICB0b3BfMTAgPSBmbG9vcihucm93KGFubnVhbF92YWwpKjAuMSkKICBhbm51YWxfdmFsID0gYW5udWFsX3ZhbFtvcmRlcihhbm51YWxfdmFsJHBvcHVsYXJpdHksIGRlY3JlYXNpbmc9VFJVRSksXQogIGFubnVhbF92YWwgPSBhbm51YWxfdmFsWzE6dG9wXzEwLF0KCiAgZm9yKG5hbWUgaW4gZmVhdHVyZV9uYW1lcyl7CiAgICB2YWxzID0gcXVhbnRpbGUoYW5udWFsX3ZhbFssbmFtZV0pWzI6NF0KICAgIHExcyA8LSByYmluZChxMXMsIGMoaSwgbmFtZSwgdmFsc1sxXSkpCiAgICBtZWRpYW5zIDwtIHJiaW5kKG1lZGlhbnMsIGMoaSwgbmFtZSwgdmFsc1syXSkpCiAgICBxM3MgPC0gcmJpbmQocTNzLCBjKGksIG5hbWUsIHZhbHNbM10pKQogIH0KfQoKY29sbmFtZXMocTFzKSA8LWMoIlllYXIiLCAiRmVhdHVyZSIsICJWYWx1ZSIpCmNvbG5hbWVzKG1lZGlhbnMpIDwtYygiWWVhciIsICJGZWF0dXJlIiwgIlZhbHVlIikKY29sbmFtZXMocTNzKSA8LWMoIlllYXIiLCAiRmVhdHVyZSIsICJWYWx1ZSIpCgpzcGxpdF9xMXMgPC0gc3BsaXQocTFzLCBxMXMkRmVhdHVyZSkKc3BsaXRfbWVkaWFucyA8LSBzcGxpdChtZWRpYW5zLCBtZWRpYW5zJEZlYXR1cmUpCnNwbGl0X3EzcyA8LSBzcGxpdChxM3MsIHEzcyRGZWF0dXJlKQoKZmVhdHVyZV9xdWFudGlsZXMgPSBhcnJheSgwLCBjKDEwMCwzLGxlbmd0aChmZWF0dXJlX25hbWVzKSkpCgpmb3IoaSBpbiAxOmxlbmd0aChmZWF0dXJlX25hbWVzKSl7CiAgZm9yKGogaW4gMToxMDApewogICAgZmVhdHVyZV9xdWFudGlsZXNbaiwxLGldID0gc3BsaXRfcTFzW1tpXV1bWzNdXVtqXQogICAgZmVhdHVyZV9xdWFudGlsZXNbaiwyLGldID0gc3BsaXRfbWVkaWFuc1tbaV1dW1szXV1bal0KICAgIGZlYXR1cmVfcXVhbnRpbGVzW2osMyxpXSA9IHNwbGl0X3Ezc1tbaV1dW1szXV1bal0KICB9Cn0KCmRpbW5hbWVzKGZlYXR1cmVfcXVhbnRpbGVzKSA8LSBsaXN0KDE5MjE6MjAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiUTEiLCAiTWVkaWFuIiwgIlEzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVfbmFtZXMpCgoKcGxvdCgxOTMwLDEsIHhsaW0gPSBjKDE5MjEsMjAyMCksIHlsaW0gPSByYW5nZSgwLDE3KSwgeGxhYj0iWWVhciIsIHlsYWI9IlJhbmdlIikKZm9yKGkgaW4gMTpsZW5ndGgoZmVhdHVyZV9uYW1lcykpewogIG5hbWUgPSBmZWF0dXJlX25hbWVzW2ldCiAgbWVkaWFucyA9IGFzLm51bWVyaWMoZmVhdHVyZV9xdWFudGlsZXNbLCxuYW1lXVssIk1lZGlhbiJdKQogIGF2ZyA9IG1lYW4obWVkaWFucykKICBtZWRpYW5zID0gbWVkaWFucyAvIGF2ZwogIAogIGxpbmVzKHg9MTkyMToyMDIwLCB5PW1lZGlhbnMsIGNvbD1pKQp9CmxlZ2VuZCgidG9wcmlnaHQiLCBsdHk9MSwgY29sPWMoMSwyLDMsNCw1LDYsNyw4LDksMTApLCBsZWdlbmQ9ZmVhdHVyZV9uYW1lcykKYGBgCiMjIyNJbnNpZ2h0czogIApCb3RoIHRoZXNlIGdyYXBocyBhcHBlYXIgdG8gaGF2ZSBzaW1pbGFyIHNoYXBlcywgd2l0aCBpbnN0cnVtZW50YWxuZXNzIGJlaW5nIHRoZSBtb3N0IHdpZGVseSB2YXJ5aW5nLCBhbmQgdGhlIHJlc3QgdmFyeWluZyBtYXJnaW5hbGx5LiBIb3dldmVyLCBmb3IgdGhlIHNwZWVjaGluZXNzIHZhcmlhYmxlLAoKCgojIyBOb3cgcGxvdHRpbmcgYWJzb2x1dGUgdmFsdWVzIGZvciBlYWNoIGZlYXR1cmUgYXMgdGhlIG1lZGlhbiB2YWx1ZXMgZm9yIG92ZXJhbGwsIGFzIHdlbGwgYXMganVzdCB0aGUgbW9zdCBwb3B1bGFyIHNvbmdzLgojIyMgTUFQRSBpcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBtb3N0IHBvcHVsYXIgbWVkaWFuIGFuZCB0aGUgb3ZlcmFsbCBtZWRpYW47IFIgaXMgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gb3ZlcmFsbCBtZWRpYW4gYW5kIG1vc3QgcG9wdWxhciBtZWRpYW4uCiMjIyBUaGVzZSB2YWx1ZXMgd2lsbCBkZXRlcm1pbmUgd2hldGhlciBvciBub3QgdGhlIG92ZXJhbGwgbWVkaWFuIGlzIGEgdXNlZnVsIHByZWRpY3RvciBvZiB3aGF0IHRoZSBtZWRpYW4gZm9yIHBvcHVsYXIgc29uZ3Mgd2lsbCBiZS4gVGhpcyBpcyBpbXBvcnRhbnQgYmVjYXVzZSBpdCB3aWxsIGJlIGVhc2llciB0byBmb3JlY2FzdCBvdmVyYWxsIHZhbHVlcyBpbnRvIHRoZSBmdXR1cmUsIGdpdmVuIHRoZXkgYXJlIG11Y2ggc21vb3RoZXIsIGFuZCB0aGVzZSBmb3JlY2FzdCBvdmVyYWxsIHZhbHVlcyBjYW4gYmUgdXNlZCB0byBwcmVkaWN0IHRoZSBtb3N0IHBvcHVsYXIgZmVhdHVyZSB2YWx1ZS4KYGBge3J9CiMgR2V0IHRoZSBsaXN0IG9mIGZlYXR1cmVzIEkgbmVlZCB0byB3b3JrIHdpdGgKCmxpYnJhcnkoTWV0cmljcykKCnExcyA9IGRhdGEuZnJhbWUobWF0cml4KG5jb2w9MywgbnJvdz0wKSkKbWVkaWFucyA9IGRhdGEuZnJhbWUobWF0cml4KG5jb2w9MywgbnJvdz0wKSkKYmVzdF9tZWRpYW5zID0gZGF0YS5mcmFtZShtYXRyaXgobmNvbD0zLCBucm93PTApKQpxM3MgPSBkYXRhLmZyYW1lKG1hdHJpeChuY29sPTMsIG5yb3c9MCkpCmZlYXR1cmVfbmFtZXMgPSBzb3J0KGMoImFjb3VzdGljbmVzcyIsICJkYW5jZWFiaWxpdHkiLCAiaW5zdHJ1bWVudGFsbmVzcyIsICJlbmVyZ3kiLAogICAgICAgICAgICAgICAgICAiZHVyYXRpb25fbXMiLCAidmFsZW5jZSIsICJ0ZW1wbyIsICJsaXZlbmVzcyIsICJsb3VkbmVzcyIsICJzcGVlY2hpbmVzcyIpKQpmb3IoaSBpbiAxOTIxOjIwMjApewogIGFubnVhbF92YWwgPSBzdWJzZXQoZGYsIHllYXI9PWkpCiAgdG9wXzEwID0gZmxvb3IobnJvdyhhbm51YWxfdmFsKSowLjEpCiAgYW5udWFsX3ZhbCA9IGFubnVhbF92YWxbb3JkZXIoYW5udWFsX3ZhbCRwb3B1bGFyaXR5LCBkZWNyZWFzaW5nPVRSVUUpLF0KICBiZXN0X29uZXMgPSBhbm51YWxfdmFsWzE6dG9wXzEwLF0KCiAgZm9yKG5hbWUgaW4gZmVhdHVyZV9uYW1lcyl7CiAgICB2YWxzID0gcXVhbnRpbGUoYW5udWFsX3ZhbFssbmFtZV0pWzI6NF0KICAgIGJlc3RfdmFscyA9IHF1YW50aWxlKGJlc3Rfb25lc1ssbmFtZV0pWzI6NF0KICAgIGJlc3RfbWVkaWFucyA8LSByYmluZChiZXN0X21lZGlhbnMsIGMoaSwgbmFtZSwgYmVzdF92YWxzWzJdKSkKICAgIG1lZGlhbnMgPC0gcmJpbmQobWVkaWFucywgYyhpLCBuYW1lLCB2YWxzWzJdKSkKICB9Cn0KCmNvbG5hbWVzKG1lZGlhbnMpIDwtYygiWWVhciIsICJGZWF0dXJlIiwgIlZhbHVlIikKY29sbmFtZXMoYmVzdF9tZWRpYW5zKSA8LSBjKCJZZWFyIiwgIkZlYXR1cmUiLCAiVmFsdWUiKQoKc3BsaXRfbWVkaWFucyA8LSBzcGxpdChtZWRpYW5zLCBtZWRpYW5zJEZlYXR1cmUpCnNwbGl0X2Jlc3RfbWVkaWFucyA8LSBzcGxpdChiZXN0X21lZGlhbnMsIGJlc3RfbWVkaWFucyRGZWF0dXJlKQoKZmVhdHVyZV9xdWFudGlsZXMgPSBtYXRyaXgoMCwgbmNvbD1sZW5ndGgoZmVhdHVyZV9uYW1lcyksIG5yb3c9MTAwKQpiZXN0X2ZlYXR1cmVfcXVhbnRpbGVzID0gbWF0cml4KDAsIG5jb2w9bGVuZ3RoKGZlYXR1cmVfbmFtZXMpLCBucm93PTEwMCkKCmZvcihpIGluIDE6bGVuZ3RoKGZlYXR1cmVfbmFtZXMpKXsKICBmb3IoaiBpbiAxOjEwMCl7CiAgICBmZWF0dXJlX3F1YW50aWxlc1tqLGldIDwtIHNwbGl0X21lZGlhbnNbW2ldXVtbM11dW2pdCiAgICBiZXN0X2ZlYXR1cmVfcXVhbnRpbGVzW2osaV0gPSBzcGxpdF9iZXN0X21lZGlhbnNbW2ldXVtbM11dW2pdCiAgfQp9CgpkaW1uYW1lcyhmZWF0dXJlX3F1YW50aWxlcykgPC0gbGlzdCgxOTIxOjIwMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVfbmFtZXMpCmRpbW5hbWVzKGJlc3RfZmVhdHVyZV9xdWFudGlsZXMpIDwtIGxpc3QoMTkyMToyMDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlX25hbWVzKQoKCgoKZm9yKGkgaW4gMTpsZW5ndGgoZmVhdHVyZV9uYW1lcykpewogIG5hbWUgPSBmZWF0dXJlX25hbWVzW2ldCiAgc2NvcmVzID0gYXMubnVtZXJpYyhmZWF0dXJlX3F1YW50aWxlc1ssaV0pCiAgYmVzdF9zY29yZXMgPSBhcy5udW1lcmljKGJlc3RfZmVhdHVyZV9xdWFudGlsZXNbLGldKQogIG1hcGUgPSBtYXBlKGJlc3Rfc2NvcmVzLCBzY29yZXMpCiAgbWFwZSA9IHJvdW5kKG1hcGUgKiAxMDAsMykKICByICA9IGNvcihzY29yZXMsIGJlc3Rfc2NvcmVzKQogIHB2YWwgPSB0LnRlc3Qoc2NvcmVzLCApCiAgIyBOb3JtYWxpemUgaXQ/Pz8/PyAvIG1ha2UgaXQgYSBmdW5jdGlvbiBvZiBpdHMgb3duIG1lYW4KICAKICBzbWFsbGVzdCA9IG1pbihtaW4oc2NvcmVzKSwgbWluKGJlc3Rfc2NvcmVzKSkKICBiaWdnZXN0ID0gbWF4KG1heChzY29yZXMpLCBtYXgoYmVzdF9zY29yZXMpKQogIAogIHBsb3QoeD0xOTIxOjIwMjAsIHk9c2NvcmVzLCBjb2w9MSwgdHlwZT0ibCIsIHlsaW09YyhzbWFsbGVzdCwgYmlnZ2VzdCksIG1haW49bmFtZSwgc3ViPXBhc3RlKCJNQVBFOiAiLCBtYXBlLCAiJTsiLCAiUjoiLCByKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCiAgbGluZXMoeD0xOTIxOjIwMjAsIHk9YmVzdF9zY29yZXMsIGNvbD0yKQogIGxlZ2VuZCgidG9wcmlnaHQiLCBsdHk9MSwgY29sPWMoMSwyKSwgbGVnZW5kPWMoIk92ZXJhbGwiLCAiTW9zdCBQb3B1bGFyIikpCiAgdGl0bGUoKQp9CmBgYAojIyMjIyBJbnNpZ2h0czogCioqQWNvdXN0aWNuZXNzKio6IEEgaGlnaCBNQVBFIHdoZW4gY29tcGFyaW5nIHRoZSBtb3N0IHBvcHVsYXIgc29uZ3Mgd2l0aCB0aGUgb3ZlcmFsbCB0cmVuZHMgaW5kaWNhdGVzIHRoYXQgdGhlcmUgaXMgYSBsYXJnZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHR3byAobGlrZWx5IGNhdXNlZCBpbiB0aGUgMTkzMHMtMTk1MHMpLiBUaGUgUiB2YWx1ZSBpcyBoaWdoIHdoaWNoIG1lYW5zIHRoZXJlIGlzIGEgaGlnaCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBtb3N0IHBvcHVsYXIgc29uZ3MgYW5kIHRoZSBvdmVyYWxsIHNvbmdzLiBCZWNhdXNlIGFjb3VzdGljbmVzcyBzZWVtcyB0byBpbXBhY3QgcG9wdWxhciBzb25ncyBhbmQgdGhlcmUgaXMgYSBsYXJnZSBjaGFuZ2Ugb3ZlciB0aW1lLCB3ZSB3aWxsIG1vdmUgZm9yd2FyZCB3aXRoIHByZWRpY3RpbmcgdGhlIGFjb3VzdGljbmVzcyBvZiBzb25ncyBpbiB0aGUgZnV0dXJlLiAKKipEYW5jZWFiaWxpdHkqKjogQSBsb3cgTUFQRSBpbmRpY2F0ZXMgbm90IGEgbG90IG9mIGRpZmZlcmVuY2UgYmV0d2VlbiBwb3B1bGFyIHNvbmdzIGFuZCBvdmVyYWxsIHNvbmdzIGRhbmNlYWJpbGl0eS4gSG93ZXZlciwgYSBoaWdoIFIgdmFsdWUgbWVhbnMgdGhlcmUgaXMgYSBoaWdoIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIG1vc3QgcG9wdWxhciBzb25ncyBhbmQgdGhlIG92ZXJhbGwgc29uZ3MuIEJlY2F1c2UgZGFuY2VhYmlsaXR5IHNlZW1zIHRvIGltcGFjdCBwb3B1bGFyIHNvbmdzIChkdWUgdG8gdGhlIGhpZ2ggY29ycmVsYXRpb24pIGFuZCB0aGVyZSBpcyBhIHNvbWUgY2hhbmdlIG92ZXIgdGltZSwgd2Ugd2lsbCBtb3ZlIGZvcndhcmQgd2l0aCBwcmVkaWN0aW5nIHRoZSBkYW5jZWFiaWxpdHkgb2Ygc29uZ3MgaW4gdGhlIGZ1dHVyZS4gCioqRHVyYXRpb24gKG1zKSoqOiBUaGVyZSBpcyBhIGxvdyBNQVBFIHdoaWNoIG1lYW5zIHRoYXQgcG9wdWxhciBzb25ncyBoYXZlIGFib3V0IHRoZSBzYW1lIGxlbmd0aCBhcyBhbGwgb3RoZXIgc29uZ3MuIFRoZSBSIHZhbHVlIGlzIHJlbGF0aXZlbHkgaGlnaCB3aGljaCBtZWFucyB0aGVyZSBpcyBhIGhpZ2ggY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgbW9zdCBwb3B1bGFyIHNvbmdzIGFuZCB0aGUgb3ZlcmFsbCBzb25ncy4gQmVjYXVzZSBkdXJhdGlvbiBzZWVtcyB0byBpbXBhY3QgcG9wdWxhciBzb25ncyAoZHVlIHRvIHRoZSBoaWdoIGNvcnJlbGF0aW9uKSBhbmQgdGhlcmUgaXMgYSBzb21lIGNoYW5nZSBvdmVyIHRpbWUsIHdlIHdpbGwgbW92ZSBmb3J3YXJkIHdpdGggcHJlZGljdGluZyB0aGUgZHVyYXRpb24gb2Ygc29uZ3MgaW4gdGhlIGZ1dHVyZS4gCioqRW5lcmd5Kio6IEEgaGlnaCBNQVBFIHdoZW4gY29tcGFyaW5nIHRoZSBtb3N0IHBvcHVsYXIgc29uZ3Mgd2l0aCB0aGUgb3ZlcmFsbCB0cmVuZHMgaW5kaWNhdGVzIHRoYXQgdGhlcmUgaXMgYSBsYXJnZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHR3by4gVGhlIFIgdmFsdWUgaXMgaGlnaCB3aGljaCBtZWFucyB0aGVyZSBpcyBhIGhpZ2ggY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgbW9zdCBwb3B1bGFyIHNvbmdzIGFuZCB0aGUgb3ZlcmFsbCBzb25ncy4gQmVjYXVzZSBlbmVyZ3kgc2VlbXMgdG8gaW1wYWN0IHBvcHVsYXIgc29uZ3MgKGR1ZSB0byB0aGUgaGlnaCBNQVBFIGFuZCBjb3JyZWxhdGlvbikgYW5kIHRoZXJlIGFyZSBsYXJnZSBjaGFuZ2VzIG92ZXIgdGltZSwgd2Ugd2lsbCBtb3ZlIGZvcndhcmQgd2l0aCBwcmVkaWN0aW5nIHRoZSBlbmVyZ3kgb2Ygc29uZ3MgaW4gdGhlIGZ1dHVyZS4gCioqSW5zdHJ1bWVudGFsbmVzcyoqOiBUaGUgcG9wdWxhciBzb25ncyBhbmQgb3ZlcmFsbCBtdXNpYyB0cmVuZHMgYXJlIHByZXR0eSBtdWNoIHRoZSBleGFjdCBzYW1lIGFmdGVyIHRoZSAxOTUwcywgaW5kaWNhdGluZyB0aGF0IGluc3RydW1lbnRhbG5lc3MgZG9lcyBub3QgYWZmZWN0IHBvcHVsYXJpdHkuIEJlY2F1c2UgaW5zdHJ1bWVudGFsbmVzcyBkb2VzIG5vdCBpbXBhY3QgcG9wdWxhciBzb25ncyBhbmQgdGhlcmUgaXMgYSBmbGF0bGluZSBhZnRlciB0aGUgMTk1MHMgKGluZGljYXRpbmcgbm8gY2hhbmdlIG92ZXIgdGltZSksIHdlIHdpbGwgbm90IG1vdmUgZm9yd2FyZCB3aXRoIHByZWRpY3RpbmcgdGhlIGluc3RydW1lbnRhbG5lc3Mgb2Ygc29uZ3MgaW4gdGhlIGZ1dHVyZS4gCioqTGl2ZW5lc3MqKjogQSBoaWdoIE1BUEUgd2hlbiBjb21wYXJpbmcgdGhlIG1vc3QgcG9wdWxhciBzb25ncyB3aXRoIHRoZSBvdmVyYWxsIHRyZW5kcyBpbmRpY2F0ZXMgdGhhdCB0aGVyZSBpcyBhIGxhcmdlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdHdvLiBUaGUgUiB2YWx1ZSBpcyBoaWdoIHdoaWNoIG1lYW5zIHRoZXJlIGlzIGEgaGlnaCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBtb3N0IHBvcHVsYXIgc29uZ3MgYW5kIHRoZSBvdmVyYWxsIHNvbmdzLiBCZWNhdXNlIGxpdmVuZXNzIHNlZW1zIHRvIGltcGFjdCBwb3B1bGFyIHNvbmdzIChkdWUgdG8gdGhlIGhpZ2ggTUFQRSBhbmQgY29ycmVsYXRpb24pIGFuZCB0aGVyZSBpcyBhIHNvbWUgY2hhbmdlIG92ZXIgdGltZSwgd2Ugd2lsbCBtb3ZlIGZvcndhcmQgd2l0aCBwcmVkaWN0aW5nIHRoZSBsaXZlbmVzcyBvZiBzb25ncyBpbiB0aGUgZnV0dXJlLiAKKipMb3VkbmVzcyoqOiBBIGhpZ2ggTUFQRSB3aGVuIGNvbXBhcmluZyB0aGUgbW9zdCBwb3B1bGFyIHNvbmdzIHdpdGggdGhlIG92ZXJhbGwgdHJlbmRzIGluZGljYXRlcyB0aGF0IHRoZXJlIGlzIGEgbGFyZ2UgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28uIFRoZSBSIHZhbHVlIGlzIGhpZ2ggd2hpY2ggbWVhbnMgdGhlcmUgaXMgYSBoaWdoIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIG1vc3QgcG9wdWxhciBzb25ncyBhbmQgdGhlIG92ZXJhbGwgc29uZ3MuIEJlY2F1c2UgbG91ZG5lc3Mgc2VlbXMgdG8gaW1wYWN0IHBvcHVsYXIgc29uZ3MgKGR1ZSB0byB0aGUgaGlnaCBNQVBFIGFuZCBjb3JyZWxhdGlvbikgYW5kIHRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgY2hhbmdlIG92ZXIgdGltZSwgd2Ugd2lsbCBtb3ZlIGZvcndhcmQgd2l0aCBwcmVkaWN0aW5nIHRoZSBsb3VkbmVzcyBvZiBzb25ncyBpbiB0aGUgZnV0dXJlLiAKKipTcGVlY2hpbmVzcyoqOiBBIGhpZ2ggTUFQRSB3aGVuIGNvbXBhcmluZyB0aGUgbW9zdCBwb3B1bGFyIHNvbmdzIHdpdGggdGhlIG92ZXJhbGwgdHJlbmRzIGluZGljYXRlcyB0aGF0IHRoZXJlIGlzIGEgbGFyZ2UgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28uIEl0J3MgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCB0aGUgZHJpdmVyIG9mIHRoZSBoaWdoIE1BUEUgaXMgZHVlIHRvIHRoZSB0d28gbGFyZ2Ugc3Bpa2VzIGJldHdlZW4gMTkzMCBhbmQgMTk1MCwgd2hpbGUgc3BlZWNoaW5lc3MgbGV2ZWxzIGJldHdlZW4gcG9wdWxhciBzb25ncyBhbmQgdGhlIG92ZXJhbGwgaW5kdXN0cnkgaW4gdGhlIGxhc3QgNzAgeWVhcnMgaGF2ZSBnZW5lcmFsbHkgYmVlbiB0aGUgc2FtZS4gVGhlIFIgdmFsdWUgaXMgbG93IHdoaWNoIG1lYW5zIHRoZXJlIGlzIGEgbG93IGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIG1vc3QgcG9wdWxhciBzb25ncyBhbmQgdGhlIG92ZXJhbGwgc29uZ3MuIFdlIHdpbGwgbm90IG1vdmUgZm9yd2FyZCB3aXRoIHByZWRpY3Rpbmcgc3BlZWNoaW5lc3MsIGR1ZSB0byB0aGUgbGFjayBvZiBjaGFuZ2UgaW4gc3BlZWNoaW5lc3Mgb3ZlciB0aW1lIGFuZCBsb3cgUi4KKipUZW1wbyoqOiBUaGUgTUFQRSBpcyBsb3csIGluaWRjYXRpbmcgdGhhdCB0aGVyZSBpc24ndCBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgaW4gdGVtcG8gYmV0d2VlbiB0aGUgcG9wdWxhciBzb25ncyBhbmQgdGhlIG92ZXJhbGwgbXVzaWMgdHJlbmRzLiBUaGUgUiBpcyByZWxhdGl2ZWx5IGhpZ2gsIHNvIHRoZXJlIGlzIHBvc3NpYmx5IGEgY29ycmVsYXRpb24gYmV0d2VlbiBwb3B1bGFyIHNvbmdzIGFuZCB0aGUgb3ZlcmFsbCBzb25ncy4gQmVjYXVzZSB0ZW1wbyBzZWVtcyB0byBpbXBhY3QgcG9wdWxhciBzb25ncyAoZHVlIHRvIHRoZSBoaWdoIGNvcnJlbGF0aW9uKSBhbmQgdGhlcmUgaXMgYSBzb21lIGNoYW5nZSBvdmVyIHRpbWUsIHdlIHdpbGwgbW92ZSBmb3J3YXJkIHdpdGggcHJlZGljdGluZyB0aGUgdGVtcG8gb2Ygc29uZ3MgaW4gdGhlIGZ1dHVyZS4gCioqVmFsZW5jZSoqOiBBIGhpZ2ggTUFQRSB3aGVuIGNvbXBhcmluZyB0aGUgbW9zdCBwb3B1bGFyIHNvbmdzIHdpdGggdGhlIG92ZXJhbGwgdHJlbmRzIGluZGljYXRlcyB0aGF0IHRoZXJlIGlzIGEgbGFyZ2UgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28gaW4gdGVybXMgb2YgdmFsZW5jZS4gVGhlIFIgdmFsdWUgaXMgaGlnaCB3aGljaCBtZWFucyB0aGVyZSBpcyBhIGhpZ2ggY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgbW9zdCBwb3B1bGFyIHNvbmdzIGFuZCB0aGUgb3ZlcmFsbCBzb25ncy4gQmVjYXVzZSB2YWxlbmNlIHNlZW1zIHRvIGltcGFjdCBwb3B1bGFyIHNvbmdzIChkdWUgdG8gdGhlIGhpZ2ggTUFQRSBhbmQgaGlnaCBjb3JyZWxhdGlvbikgYW5kIHRoZXJlIGlzIGEgc29tZSBjaGFuZ2Ugb3ZlciB0aW1lLCB3ZSB3aWxsIG1vdmUgZm9yd2FyZCB3aXRoIHByZWRpY3RpbmcgdGhlIHZhbGVuY2Ugb2Ygc29uZ3MgaW4gdGhlIGZ1dHVyZS4gCgoKCiMgVHJhaW4gdGltZSBzZXJpZXMgbW9kZWxzIHRvIGZvcmVjYXN0IHRoZSBmdXR1cmUgbW9kZWxzLgpgYGB7cn0KIyBTZXQgdXAgdGhlIGxpYnJhcmllcyBhbmQgdGhlIHRyYWluaW5nL3Rlc3RpbmcgYW1vdW50cwpsaWJyYXJ5KCJzbW9vdGgiKQpsaWJyYXJ5KCJmb3JlY2FzdCIpCmxpYnJhcnkoIm5uZm9yIikKdHJhaW5pbmcucGVyY2VudCA9IDAuOTUKblRyYWluID0gMTAwKnRyYWluaW5nLnBlcmNlbnQKblRlc3QgPSAxMDAqKDEtdHJhaW5pbmcucGVyY2VudCkKYGBgCgojIyBBY2NvdXN0aWNuZXNzIE1vZGVscwoKYGBge3J9CmFjb3VzdGljbmVzcyA9IHRzKGFzLm51bWVyaWMoZmVhdHVyZV9xdWFudGlsZXNbLCJhY291c3RpY25lc3MiXSkgLCBzdGFydD0xOTIxKQphY291c3RpY25lc3MudHJhaW4gPSBzdWJzZXQoYWNvdXN0aWNuZXNzLCBzdGFydD0xLCBlbmQ9blRyYWluKQphY291c3RpY25lc3MudGVzdCA9IHN1YnNldChhY291c3RpY25lc3MsIHN0YXJ0ID0gKG5UcmFpbisxKSwgZW5kID0oblRyYWluK25UZXN0KSkKCiNzZXMKYWNvdXN0aWNuZXNzLnRyYWluLnNlcyA8LSBzZXMoYWNvdXN0aWNuZXNzLnRyYWluLCBoPW5UZXN0KQphY291c3RpY25lc3Muc2VzLm1hcGUgPSBtYXBlKGFjb3VzdGljbmVzcy50cmFpbi5zZXMkbWVhbiwgYWNvdXN0aWNuZXNzLnRlc3QpCnBsb3QoYWNvdXN0aWNuZXNzLnRyYWluLnNlcywgbWFpbj1wYXN0ZSgiQWNvdXN0aWNuZXNzIiwgIlNFUyIpLCBzdWI9cGFzdGUoIk1BUEU6Iiwgcm91bmQoYWNvdXN0aWNuZXNzLnNlcy5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyhhY291c3RpY25lc3MpCgojU0FSSU1BCmFjb3VzdGljbmVzcy5zYXJpbWEubW9kZWwgPC0gYXV0by5hcmltYShhY291c3RpY25lc3MudHJhaW4pCmFjb3VzdGljbmVzcy5zYXJpbWEgPC0gZm9yZWNhc3QoYWNvdXN0aWNuZXNzLnNhcmltYS5tb2RlbCwgaD1uVGVzdCkKYWNvdXN0aWNuZXNzLnNhcmltYS5tYXBlID0gbWFwZShhY291c3RpY25lc3Muc2FyaW1hJG1lYW4sIGFjb3VzdGljbmVzcy50ZXN0KQpwbG90KGFjb3VzdGljbmVzcy5zYXJpbWEsIG1haW49cGFzdGUoIkFjb3VzdGljbmVzcyIsICJTQVJJTUEiKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGFjb3VzdGljbmVzcy5zYXJpbWEubWFwZSoxMDAsMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyhhY291c3RpY25lc3MpCgojTmV1cmFsIE5ldHdvcmsKYWNvdXN0aWNuZXNzLnRyYWluLm5uIDwtIGVsbShhY291c3RpY25lc3MudHJhaW4pCmFjb3VzdGljbmVzcy5ubi5mb3JlY2FzdCA8LSBmb3JlY2FzdChhY291c3RpY25lc3MudHJhaW4ubm4sIGg9blRlc3QpCmFjb3VzdGljbmVzcy5ubi5tYXBlID0gbWFwZShhY291c3RpY25lc3Mubm4uZm9yZWNhc3QkbWVhbiwgYWNvdXN0aWNuZXNzLnRlc3QpCnBsb3QoYWNvdXN0aWNuZXNzLCBtYWluPXBhc3RlKCJBY291c3RpY25lc3MiLCAiTk4iKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGFjb3VzdGljbmVzcy5ubi5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyh0cyhhY291c3RpY25lc3Mubm4uZm9yZWNhc3QkbWVhbiwgc3RhcnQ9MTkyMStuVHJhaW4pLCBjb2w9MikKYGBgCkhlcmUgd2Ugc2VlIHRoYXQgdGhlIGJlc3QgbWV0aG9kIHRvIHByZWRpY3QgZnV0dXJlIGFjb3VzdGljbmVzcyBtZWRpYW4gdmFsdWVzIGZvciB0aGUgb3ZlcmFsbCBkYXRhIHNldCBpcyB0aHJvdWdoIFNFUy4gSG93ZXZlciwgZ2l2ZW4gdGhhdCB0aGlzIGJlc3QgTUFQRSBpcyByb3VnaGx5IDM3JSwgdGhpcyBmZWF0dXJlIGNhbm5vdCBiZSByZWxpYWJseSBmb3JlY2FzdCBpbnRvIHRoZSBmdXR1cmUuCiAgCgoKCiMjIEVuZXJneSBtb2RlbHMKYGBge3J9CmVuZXJneSA9IHRzKGFzLm51bWVyaWMoZmVhdHVyZV9xdWFudGlsZXNbLCJlbmVyZ3kiXSkgLCBzdGFydD0xOTIxKQplbmVyZ3kudHJhaW4gPSBzdWJzZXQoZW5lcmd5LCBzdGFydD0xLCBlbmQ9blRyYWluKQplbmVyZ3kudGVzdCA9IHN1YnNldChlbmVyZ3ksIHN0YXJ0ID0gKG5UcmFpbisxKSwgZW5kID0oblRyYWluK25UZXN0KSkKCiNTRVMKZW5lcmd5LnRyYWluLnNlcyA8LSBzZXMoZW5lcmd5LnRyYWluLCBoPW5UZXN0KQplbmVyZ3kuc2VzLm1hcGUgPSBtYXBlKGVuZXJneS50cmFpbi5zZXMkbWVhbiwgZW5lcmd5LnRlc3QpCnBsb3QoZW5lcmd5LnRyYWluLnNlcywgbWFpbj1wYXN0ZSgiRW5lcmd5IiwgIlNFUyIpLCBzdWI9cGFzdGUoIk1BUEU6Iiwgcm91bmQoZW5lcmd5LnNlcy5tYXBlKiAxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXMoZW5lcmd5KQoKI1NBUklNQQplbmVyZ3kuc2FyaW1hLm1vZGVsIDwtIGF1dG8uYXJpbWEoZW5lcmd5LnRyYWluKQplbmVyZ3kuc2FyaW1hIDwtIGZvcmVjYXN0KGVuZXJneS5zYXJpbWEubW9kZWwsIGg9blRlc3QpCmVuZXJneS5zYXJpbWEubWFwZSA9IG1hcGUoZW5lcmd5LnNhcmltYSRtZWFuLCBlbmVyZ3kudGVzdCkKcGxvdChlbmVyZ3kuc2FyaW1hLCBtYWluPXBhc3RlKCJFbmVyZ3kiLCAiU0FSSU1BIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZChlbmVyZ3kuc2FyaW1hLm1hcGUqIDEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyhlbmVyZ3kpCgojTmV1cmFsIE5ldHdvcmsKZW5lcmd5LnRyYWluLm5uIDwtIGVsbShlbmVyZ3kudHJhaW4pCmVuZXJneS5ubi5mb3JlY2FzdCA8LSBmb3JlY2FzdChlbmVyZ3kudHJhaW4ubm4sIGg9blRlc3QpCmVuZXJneS5ubi5tYXBlID0gbWFwZShlbmVyZ3kubm4uZm9yZWNhc3QkbWVhbiwgZW5lcmd5LnRlc3QpCnBsb3QoZW5lcmd5LCBtYWluPXBhc3RlKCJFbmVyZ3kiLCAiTk4iKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGVuZXJneS5ubi5tYXBlKiAxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXModHMoZW5lcmd5Lm5uLmZvcmVjYXN0JG1lYW4sIHN0YXJ0PTE5MjErblRyYWluKSwgY29sPTIpCmBgYApIZXJlIHdlIHNlZSB0aGF0IHRoZSBiZXN0IG1ldGhvZCB0byBwcmVkaWN0IGZ1dHVyZSBlbmVyZ3kgbWVkaWFuIHZhbHVlcyBmb3IgdGhlIG92ZXJhbGwgZGF0YSBzZXQgaXMgdGhyb3VnaCBTRVMuIEdpdmVuIHRoYXQgdGhpcyBiZXN0IE1BUEUgaXMgcm91Z2hseSA4JSwgdGhpcyBmZWF0dXJlIGNhbiBiZSBmYWlybHkgcmVsaWFibHkgZm9yZWNhc3QgaW50byB0aGUgZnV0dXJlLgogIAoKIyMgRGFuY2VhYmlsaXR5IG1vZGVscwpgYGB7cn0KZGFuY2VhYmlsaXR5ID0gdHMoYXMubnVtZXJpYyhmZWF0dXJlX3F1YW50aWxlc1ssImRhbmNlYWJpbGl0eSJdKSAsIHN0YXJ0PTE5MjEpCmRhbmNlYWJpbGl0eS50cmFpbiA9IHN1YnNldChkYW5jZWFiaWxpdHksIHN0YXJ0PTEsIGVuZD1uVHJhaW4pCmRhbmNlYWJpbGl0eS50ZXN0ID0gc3Vic2V0KGRhbmNlYWJpbGl0eSwgc3RhcnQgPSAoblRyYWluKzEpLCBlbmQgPShuVHJhaW4rblRlc3QpKQoKI1NFUwpkYW5jZWFiaWxpdHkudHJhaW4uc2VzIDwtIHNlcyhkYW5jZWFiaWxpdHkudHJhaW4sIGg9blRlc3QpCmRhbmNlYWJpbGl0eS5zZXMubWFwZSA9IG1hcGUoZGFuY2VhYmlsaXR5LnRyYWluLnNlcyRtZWFuLCBkYW5jZWFiaWxpdHkudGVzdCkKcGxvdChkYW5jZWFiaWxpdHkudHJhaW4uc2VzLCBtYWluPXBhc3RlKCJkYW5jZWFiaWxpdHkiLCAiU0VTIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZChkYW5jZWFiaWxpdHkuc2VzLm1hcGUqIDEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyhkYW5jZWFiaWxpdHkpCgojU0FSSU1BCmRhbmNlYWJpbGl0eS5zYXJpbWEubW9kZWwgPC0gYXV0by5hcmltYShkYW5jZWFiaWxpdHkudHJhaW4pCmRhbmNlYWJpbGl0eS5zYXJpbWEgPC0gZm9yZWNhc3QoZGFuY2VhYmlsaXR5LnNhcmltYS5tb2RlbCwgaD1uVGVzdCkKZGFuY2VhYmlsaXR5LnNhcmltYS5tYXBlID0gbWFwZShkYW5jZWFiaWxpdHkuc2FyaW1hJG1lYW4sIGRhbmNlYWJpbGl0eS50ZXN0KQpwbG90KGRhbmNlYWJpbGl0eS5zYXJpbWEsIG1haW49cGFzdGUoImRhbmNlYWJpbGl0eSIsICJTQVJJTUEiKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGRhbmNlYWJpbGl0eS5zYXJpbWEubWFwZSogMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKGRhbmNlYWJpbGl0eSkKCiNOZXVyYWwgTmV0d29yawpkYW5jZWFiaWxpdHkudHJhaW4ubm4gPC0gZWxtKGRhbmNlYWJpbGl0eS50cmFpbikKZGFuY2VhYmlsaXR5Lm5uLmZvcmVjYXN0IDwtIGZvcmVjYXN0KGRhbmNlYWJpbGl0eS50cmFpbi5ubiwgaD1uVGVzdCkKZGFuY2VhYmlsaXR5Lm5uLm1hcGUgPSBtYXBlKGRhbmNlYWJpbGl0eS5ubi5mb3JlY2FzdCRtZWFuLCBkYW5jZWFiaWxpdHkudGVzdCkKcGxvdChkYW5jZWFiaWxpdHksIG1haW49cGFzdGUoImRhbmNlYWJpbGl0eSIsICJOTiIpLCBzdWI9cGFzdGUoIk1BUEU6Iiwgcm91bmQoZGFuY2VhYmlsaXR5Lm5uLm1hcGUqIDEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyh0cyhkYW5jZWFiaWxpdHkubm4uZm9yZWNhc3QkbWVhbiwgc3RhcnQ9MTkyMStuVHJhaW4pLCBjb2w9MikKYGBgCkhlcmUgd2Ugc2VlIHRoYXQgdGhlIGJlc3QgbWV0aG9kIHRvIHByZWRpY3QgZnV0dXJlIGRhbmNlYWJpbGl0eSBtZWRpYW4gdmFsdWVzIGZvciB0aGUgb3ZlcmFsbCBkYXRhIHNldCBpcyB0aHJvdWdoIE5OLiBIb3dldmVyLCBnaXZlbiB0aGF0IHRoaXMgYmVzdCBNQVBFIGlzIHJvdWdobHkgMTAuNCUsIHRoaXMgZmVhdHVyZSBjYW5ub3QgYmUgcmVsaWFibHkgZm9yZWNhc3QgaW50byB0aGUgZnV0dXJlLgogIAoKIyMgRHVyYXRpb24gbW9kZWxzCgpgYGB7cn0KZHVyYXRpb24gPSB0cyhhcy5udW1lcmljKGZlYXR1cmVfcXVhbnRpbGVzWywiZHVyYXRpb25fbXMiXSkgLCBzdGFydD0xOTIxKQpkdXJhdGlvbi50cmFpbiA9IHN1YnNldChkdXJhdGlvbiwgc3RhcnQ9MSwgZW5kPW5UcmFpbikKZHVyYXRpb24udGVzdCA9IHN1YnNldChkdXJhdGlvbiwgc3RhcnQgPSAoblRyYWluKzEpLCBlbmQgPShuVHJhaW4rblRlc3QpKQoKI1NFUwpkdXJhdGlvbi50cmFpbi5zZXMgPC0gc2VzKGR1cmF0aW9uLnRyYWluLCBoPW5UZXN0KQpkdXJhdGlvbi5zZXMubWFwZSA9IG1hcGUoZHVyYXRpb24udHJhaW4uc2VzJG1lYW4sIGR1cmF0aW9uLnRlc3QpCnBsb3QoZHVyYXRpb24udHJhaW4uc2VzLCBtYWluPXBhc3RlKCJEdXJhdGlvbiIsICJTRVMiKSwgc3ViPXBhc3RlKCJNQVBFOiIscm91bmQoZHVyYXRpb24uc2VzLm1hcGUqMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKGR1cmF0aW9uKQoKI1NBUklNQQpkdXJhdGlvbi5zYXJpbWEubW9kZWwgPC0gYXV0by5hcmltYShkdXJhdGlvbi50cmFpbikKZHVyYXRpb24uc2FyaW1hIDwtIGZvcmVjYXN0KGR1cmF0aW9uLnNhcmltYS5tb2RlbCwgaD1uVGVzdCkKZHVyYXRpb24uc2FyaW1hLm1hcGUgPSBtYXBlKGR1cmF0aW9uLnNhcmltYSRtZWFuLCBkdXJhdGlvbi50ZXN0KQpwbG90KGR1cmF0aW9uLnNhcmltYSwgbWFpbj1wYXN0ZSgiRHVyYXRpb24iLCAiU0FSSU1BIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZChkdXJhdGlvbi5zYXJpbWEubWFwZSoxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXMoZHVyYXRpb24pCgoKI05ldXJhbCBOZXR3b3JrCmR1cmF0aW9uLnRyYWluLm5uIDwtIGVsbShkdXJhdGlvbi50cmFpbikKZHVyYXRpb24ubm4uZm9yZWNhc3QgPC0gZm9yZWNhc3QoZHVyYXRpb24udHJhaW4ubm4sIGg9blRlc3QpCmR1cmF0aW9uLm5uLm1hcGUgPSBtYXBlKGR1cmF0aW9uLm5uLmZvcmVjYXN0JG1lYW4sIGR1cmF0aW9uLnRlc3QpCnBsb3QoZHVyYXRpb24sIG1haW49cGFzdGUoIkR1cmF0aW9uIiwgIk5OIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZChkdXJhdGlvbi5ubi5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyh0cyhkdXJhdGlvbi5ubi5mb3JlY2FzdCRtZWFuLCBzdGFydD0xOTIxK25UcmFpbiksIGNvbD0yKQpgYGAKSGVyZSB3ZSBzZWUgdGhhdCB0aGUgYmVzdCBtZXRob2QgdG8gcHJlZGljdCBmdXR1cmUgZHVyYXRpb24gbWVkaWFuIHZhbHVlcyBmb3IgdGhlIG92ZXJhbGwgZGF0YSBzZXQgaXMgdGhyb3VnaCBOTi4gR2l2ZW4gdGhhdCB0aGlzIGJlc3QgTUFQRSBpcyByb3VnaGx5IDglLCB0aGlzIGZlYXR1cmUgY2FuIGJlIGZhaXJseSByZWxpYWJseSBmb3JlY2FzdCBpbnRvIHRoZSBmdXR1cmUuCiAgCgojIyBWYWxlbmNlCmBgYHtyfQp2YWxlbmNlID0gdHMoYXMubnVtZXJpYyhmZWF0dXJlX3F1YW50aWxlc1ssInZhbGVuY2UiXSkgLCBzdGFydD0xOTIxKQp2YWxlbmNlLnRyYWluID0gc3Vic2V0KHZhbGVuY2UsIHN0YXJ0PTEsIGVuZD1uVHJhaW4pCnZhbGVuY2UudGVzdCA9IHN1YnNldCh2YWxlbmNlLCBzdGFydCA9IChuVHJhaW4rMSksIGVuZCA9KG5UcmFpbituVGVzdCkpCgojU0VTCnZhbGVuY2UudHJhaW4uc2VzIDwtIHNlcyh2YWxlbmNlLnRyYWluLCBoPW5UZXN0KQp2YWxlbmNlLnNlcy5tYXBlID0gbWFwZSh2YWxlbmNlLnRyYWluLnNlcyRtZWFuLCB2YWxlbmNlLnRlc3QpCnBsb3QodmFsZW5jZS50cmFpbi5zZXMsIG1haW49cGFzdGUoInZhbGVuY2UiLCAiU0VTIiksIHN1Yj1wYXN0ZSgiTUFQRToiLHJvdW5kKHZhbGVuY2Uuc2VzLm1hcGUqMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKHZhbGVuY2UpCgojU0FSSU1BCnZhbGVuY2Uuc2FyaW1hLm1vZGVsIDwtIGF1dG8uYXJpbWEodmFsZW5jZS50cmFpbikKdmFsZW5jZS5zYXJpbWEgPC0gZm9yZWNhc3QodmFsZW5jZS5zYXJpbWEubW9kZWwsIGg9blRlc3QpCnZhbGVuY2Uuc2FyaW1hLm1hcGUgPSBtYXBlKHZhbGVuY2Uuc2FyaW1hJG1lYW4sIHZhbGVuY2UudGVzdCkKcGxvdCh2YWxlbmNlLnNhcmltYSwgbWFpbj1wYXN0ZSgidmFsZW5jZSIsICJTQVJJTUEiKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKHZhbGVuY2Uuc2FyaW1hLm1hcGUqMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKHZhbGVuY2UpCgoKI05ldXJhbCBOZXR3b3JrCnZhbGVuY2UudHJhaW4ubm4gPC0gZWxtKHZhbGVuY2UudHJhaW4pCnZhbGVuY2Uubm4uZm9yZWNhc3QgPC0gZm9yZWNhc3QodmFsZW5jZS50cmFpbi5ubiwgaD1uVGVzdCkKdmFsZW5jZS5ubi5tYXBlID0gbWFwZSh2YWxlbmNlLm5uLmZvcmVjYXN0JG1lYW4sIHZhbGVuY2UudGVzdCkKcGxvdCh2YWxlbmNlLCBtYWluPXBhc3RlKCJ2YWxlbmNlIiwgIk5OIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZCh2YWxlbmNlLm5uLm1hcGUqMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKHRzKHZhbGVuY2Uubm4uZm9yZWNhc3QkbWVhbiwgc3RhcnQ9MTkyMStuVHJhaW4pLCBjb2w9MikKYGBgCiMjIyMjIEluc2lnaHRzOgpSZWxhdGl2ZWx5IGxvd2VyIE1BUEUgaW5kaWNhdGVzIHRoYXQgd2UgY2FuIHByZWRpY3QgdmFsZW5jZSBvdmVyIHRpbWUuIFRoZSBiZXN0IG1vZGVsIHRvIHVzZSBpcyB0aGUgU0FSSU1BIG1vZGVsIGJlY2F1c2UgaXQgaGFzIHRoZSBsb3dlc3QgTUFQRSBhdCA2LjclLiAKCiMjIFRlbXBvIG1vZGVsCgpgYGB7cn0KdGVtcG8gPSB0cyhhcy5udW1lcmljKGZlYXR1cmVfcXVhbnRpbGVzWywidGVtcG8iXSkgLCBzdGFydD0xOTIxKQp0ZW1wby50cmFpbiA9IHN1YnNldCh0ZW1wbywgc3RhcnQ9MSwgZW5kPW5UcmFpbikKdGVtcG8udGVzdCA9IHN1YnNldCh0ZW1wbywgc3RhcnQgPSAoblRyYWluKzEpLCBlbmQgPShuVHJhaW4rblRlc3QpKQoKI1NFUwp0ZW1wby50cmFpbi5zZXMgPC0gc2VzKHRlbXBvLnRyYWluLCBoPW5UZXN0KQp0ZW1wby5zZXMubWFwZSA9IG1hcGUodGVtcG8udHJhaW4uc2VzJG1lYW4sIHRlbXBvLnRlc3QpCnBsb3QodGVtcG8udHJhaW4uc2VzLCBtYWluPXBhc3RlKCJ0ZW1wbyIsICJTRVMiKSwgc3ViPXBhc3RlKCJNQVBFOiIscm91bmQodGVtcG8uc2VzLm1hcGUqMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKHRlbXBvKQoKI1NBUklNQQp0ZW1wby5zYXJpbWEubW9kZWwgPC0gYXV0by5hcmltYSh0ZW1wby50cmFpbikKdGVtcG8uc2FyaW1hIDwtIGZvcmVjYXN0KHRlbXBvLnNhcmltYS5tb2RlbCwgaD1uVGVzdCkKdGVtcG8uc2FyaW1hLm1hcGUgPSBtYXBlKHRlbXBvLnNhcmltYSRtZWFuLCB0ZW1wby50ZXN0KQpwbG90KHRlbXBvLnNhcmltYSwgbWFpbj1wYXN0ZSgidGVtcG8iLCAiU0FSSU1BIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZCh0ZW1wby5zYXJpbWEubWFwZSoxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXModGVtcG8pCgoKI05ldXJhbCBOZXR3b3JrCnRlbXBvLnRyYWluLm5uIDwtIGVsbSh0ZW1wby50cmFpbikKdGVtcG8ubm4uZm9yZWNhc3QgPC0gZm9yZWNhc3QodGVtcG8udHJhaW4ubm4sIGg9blRlc3QpCnRlbXBvLm5uLm1hcGUgPSBtYXBlKHRlbXBvLm5uLmZvcmVjYXN0JG1lYW4sIHRlbXBvLnRlc3QpCnBsb3QodGVtcG8sIG1haW49cGFzdGUoInRlbXBvIiwgIk5OIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZCh0ZW1wby5ubi5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyh0cyh0ZW1wby5ubi5mb3JlY2FzdCRtZWFuLCBzdGFydD0xOTIxK25UcmFpbiksIGNvbD0yKQpgYGAKIyMjIyMgSW5zaWdodHM6CkhlcmUgd2Ugc2VlIHRoYXQgdGhlIGJlc3QgbWV0aG9kIHRvIHByZWRpY3QgZnV0dXJlIHRlbXBvIG1lZGlhbiB2YWx1ZXMgZm9yIHRoZSBvdmVyYWxsIGRhdGEgc2V0IGlzIHRocm91Z2ggU0VTLiBHaXZlbiB0aGF0IHRoaXMgYmVzdCBNQVBFIGlzIHJvdWdobHkgMSUsIHRoaXMgZmVhdHVyZSBjYW4gYmUgZmFpcmx5IHJlbGlhYmx5IGZvcmVjYXN0IGludG8gdGhlIGZ1dHVyZS4KICAKCiMjIExpdmVuZXNzCmBgYHtyfQpsaXZlbmVzcyA9IHRzKGFzLm51bWVyaWMoZmVhdHVyZV9xdWFudGlsZXNbLCJsaXZlbmVzcyJdKSAsIHN0YXJ0PTE5MjEpCmxpdmVuZXNzLnRyYWluID0gc3Vic2V0KGxpdmVuZXNzLCBzdGFydD0xLCBlbmQ9blRyYWluKQpsaXZlbmVzcy50ZXN0ID0gc3Vic2V0KGxpdmVuZXNzLCBzdGFydCA9IChuVHJhaW4rMSksIGVuZCA9KG5UcmFpbituVGVzdCkpCgojU0VTCmxpdmVuZXNzLnRyYWluLnNlcyA8LSBzZXMobGl2ZW5lc3MudHJhaW4sIGg9blRlc3QpCmxpdmVuZXNzLnNlcy5tYXBlID0gbWFwZShsaXZlbmVzcy50cmFpbi5zZXMkbWVhbiwgbGl2ZW5lc3MudGVzdCkKcGxvdChsaXZlbmVzcy50cmFpbi5zZXMsIG1haW49cGFzdGUoImxpdmVuZXNzIiwgIlNFUyIpLCBzdWI9cGFzdGUoIk1BUEU6Iixyb3VuZChsaXZlbmVzcy5zZXMubWFwZSoxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXMobGl2ZW5lc3MpCgojU0FSSU1BCmxpdmVuZXNzLnNhcmltYS5tb2RlbCA8LSBhdXRvLmFyaW1hKGxpdmVuZXNzLnRyYWluKQpsaXZlbmVzcy5zYXJpbWEgPC0gZm9yZWNhc3QobGl2ZW5lc3Muc2FyaW1hLm1vZGVsLCBoPW5UZXN0KQpsaXZlbmVzcy5zYXJpbWEubWFwZSA9IG1hcGUobGl2ZW5lc3Muc2FyaW1hJG1lYW4sIGxpdmVuZXNzLnRlc3QpCnBsb3QobGl2ZW5lc3Muc2FyaW1hLCBtYWluPXBhc3RlKCJsaXZlbmVzcyIsICJTQVJJTUEiKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGxpdmVuZXNzLnNhcmltYS5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyhsaXZlbmVzcykKCgojTmV1cmFsIE5ldHdvcmsKbGl2ZW5lc3MudHJhaW4ubm4gPC0gZWxtKGxpdmVuZXNzLnRyYWluKQpsaXZlbmVzcy5ubi5mb3JlY2FzdCA8LSBmb3JlY2FzdChsaXZlbmVzcy50cmFpbi5ubiwgaD1uVGVzdCkKbGl2ZW5lc3Mubm4ubWFwZSA9IG1hcGUobGl2ZW5lc3Mubm4uZm9yZWNhc3QkbWVhbiwgbGl2ZW5lc3MudGVzdCkKcGxvdChsaXZlbmVzcywgbWFpbj1wYXN0ZSgibGl2ZW5lc3MiLCAiTk4iKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGxpdmVuZXNzLm5uLm1hcGUqMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKHRzKGxpdmVuZXNzLm5uLmZvcmVjYXN0JG1lYW4sIHN0YXJ0PTE5MjErblRyYWluKSwgY29sPTIpCmBgYAojIyMjIyBJbnNpZ2h0czoKSGVyZSB3ZSBzZWUgdGhhdCB0aGUgYmVzdCBtZXRob2QgdG8gcHJlZGljdCBmdXR1cmUgbGl2ZW5lc3MgbWVkaWFuIHZhbHVlcyBmb3IgdGhlIG92ZXJhbGwgZGF0YSBzZXQgaXMgdGhyb3VnaCBTQVJJTUEgR2l2ZW4gdGhhdCB0aGlzIGJlc3QgTUFQRSBpcyByb3VnaGx5IDIuNiUsIHRoaXMgZmVhdHVyZSBjYW4gYmUgZmFpcmx5IHJlbGlhYmx5IGZvcmVjYXN0IGludG8gdGhlIGZ1dHVyZS4KCiMjIExvdWRuZXNzCmBgYHtyfQpsb3VkbmVzcyA9IHRzKGFzLm51bWVyaWMoZmVhdHVyZV9xdWFudGlsZXNbLCJsb3VkbmVzcyJdKSAsIHN0YXJ0PTE5MjEpCmxvdWRuZXNzLnRyYWluID0gc3Vic2V0KGxvdWRuZXNzLCBzdGFydD0xLCBlbmQ9blRyYWluKQpsb3VkbmVzcy50ZXN0ID0gc3Vic2V0KGxvdWRuZXNzLCBzdGFydCA9IChuVHJhaW4rMSksIGVuZCA9KG5UcmFpbituVGVzdCkpCgojU0VTCmxvdWRuZXNzLnRyYWluLnNlcyA8LSBzZXMobG91ZG5lc3MudHJhaW4sIGg9blRlc3QpCmxvdWRuZXNzLnNlcy5tYXBlID0gbWFwZShsb3VkbmVzcy50cmFpbi5zZXMkbWVhbiwgbG91ZG5lc3MudGVzdCkKcGxvdChsb3VkbmVzcy50cmFpbi5zZXMsIG1haW49cGFzdGUoImxvdWRuZXNzIiwgIlNFUyIpLCBzdWI9cGFzdGUoIk1BUEU6Iixyb3VuZChsb3VkbmVzcy5zZXMubWFwZSoxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXMobG91ZG5lc3MpCgojU0FSSU1BCmxvdWRuZXNzLnNhcmltYS5tb2RlbCA8LSBhdXRvLmFyaW1hKGxvdWRuZXNzLnRyYWluKQpsb3VkbmVzcy5zYXJpbWEgPC0gZm9yZWNhc3QobG91ZG5lc3Muc2FyaW1hLm1vZGVsLCBoPW5UZXN0KQpsb3VkbmVzcy5zYXJpbWEubWFwZSA9IG1hcGUobG91ZG5lc3Muc2FyaW1hJG1lYW4sIGxvdWRuZXNzLnRlc3QpCnBsb3QobG91ZG5lc3Muc2FyaW1hLCBtYWluPXBhc3RlKCJsb3VkbmVzcyIsICJTQVJJTUEiKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGxvdWRuZXNzLnNhcmltYS5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyhsb3VkbmVzcykKCgojTmV1cmFsIE5ldHdvcmsKbG91ZG5lc3MudHJhaW4ubm4gPC0gZWxtKGxvdWRuZXNzLnRyYWluKQpsb3VkbmVzcy5ubi5mb3JlY2FzdCA8LSBmb3JlY2FzdChsb3VkbmVzcy50cmFpbi5ubiwgaD1uVGVzdCkKbG91ZG5lc3Mubm4ubWFwZSA9IG1hcGUobG91ZG5lc3Mubm4uZm9yZWNhc3QkbWVhbiwgbG91ZG5lc3MudGVzdCkKcGxvdChsb3VkbmVzcywgbWFpbj1wYXN0ZSgibG91ZG5lc3MiLCAiTk4iKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGxvdWRuZXNzLm5uLm1hcGUqMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKHRzKGxvdWRuZXNzLm5uLmZvcmVjYXN0JG1lYW4sIHN0YXJ0PTE5MjErblRyYWluKSwgY29sPTIpCmBgYAojIyMjIyBJbnNpZ2h0czoKSGVyZSB3ZSBzZWUgdGhhdCB0aGUgYmVzdCBtZXRob2QgdG8gcHJlZGljdCBmdXR1cmUgbG91ZG5lc3MgbWVkaWFuIHZhbHVlcyBmb3IgdGhlIG92ZXJhbGwgZGF0YSBzZXQgaXMgdGhyb3VnaCBTRVMgR2l2ZW4gdGhhdCB0aGlzIGJlc3QgTUFQRSBpcyByb3VnaGx5IDUlLCB0aGlzIGZlYXR1cmUgY2FuIGJlIGZhaXJseSByZWxpYWJseSBmb3JlY2FzdCBpbnRvIHRoZSBmdXR1cmUuCiAgCiMgTGluZWFyIE1vZGVsIGZvciBPdmVyYWxsIEZlYXR1cmUgTWVkaWFuLS0+TW9zdCBQb3B1bGFyIFNvbmdzJyBGZWF0dXJlIE1lZGlhbgojIyMgV2Ugd2lsbCBvbmx5IGNvbnNpZGVyIG1vZGVscyB0aGF0IGhhZCBhIGxvdyBlbm91Z2ggTUFQRXMgKDEwJSBjaG9zZW4gYXMgYSB0aHJlc2hvbGQpIGluIHRoZWlyIGJlc3QgdGltZXNlcmllcyBmb3JlY2FzdGluZyBtb2RlbC4KIyMjIEhlcmUgTUFQRSBpcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBhY3R1YWwgbWVkaWFuIGZvciBtb3N0IHBvcHVsYXIgc29uZ3MgYW5kIHRoZSBwcmVkaWN0ZWQgbWVkaWFuLgoKYGBge3J9CiNFbmVyZ3kKYmVzdF9lbmVyZ3kgPSB0cyhhcy5udW1lcmljKGJlc3RfZmVhdHVyZV9xdWFudGlsZXNbLCJlbmVyZ3kiXSksIHN0YXJ0PTE5MjEsIGVuZD0yMDIwKSAKYmVzdF9lbmVyZ3kudHJhaW4gPSBzdWJzZXQoYmVzdF9lbmVyZ3ksIHN0YXJ0ID0gMSwgZW5kID0gblRyYWluKQpiZXN0X2VuZXJneS50ZXN0ID0gc3Vic2V0KGJlc3RfZW5lcmd5LCBzdGFydCA9IG5UcmFpbisxLCBlbmQgPSBuVHJhaW4rblRlc3QpCnRyYWluX2RmID0gZGF0YS5mcmFtZShiZXN0ID0gYmVzdF9lbmVyZ3kudHJhaW4sIG92ZXJhbGwgPSBlbmVyZ3kudHJhaW4pCgpiZXN0ID0gYmVzdF9lbmVyZ3kudHJhaW4Kb3ZlcmFsbCA9IGVuZXJneS50cmFpbgoKZW5lcmd5LmxtIDwtIGxtKGJlc3QgfiBvdmVyYWxsKQp0ZXN0X2RmIDwtIGRhdGEuZnJhbWUob3ZlcmFsbCA9IGVuZXJneS50ZXN0KQpiZXN0X2VuZXJneS50ZXN0LnByZWRpY3QgPC0gcHJlZGljdChlbmVyZ3kubG0sIG5ld2RhdGE9dGVzdF9kZiwgaW50ZXJ2YWw9InByZWRpY3Rpb24iKQpiZXN0X2VuZXJneV90ZXN0X3ByZWRpY3QubWFwZSA8LSBtYXBlKGJlc3RfZW5lcmd5LnRlc3RbMTpsZW5ndGgoYmVzdF9lbmVyZ3kudGVzdCldLCBiZXN0X2VuZXJneS50ZXN0LnByZWRpY3RbLDFdKQpwbG90KGJlc3RfZW5lcmd5LHhsYWI9IlllYXIiLCB5bGFiPSJFbmVyZ3kiLCBtYWluPXBhc3RlKCJFbmVyZ3kgUG9wdWxhcml0eSBQcmVkaWN0aW9uIiksIHN1YiA9IHBhc3RlKCJNQVBFOiAiLCByb3VuZChiZXN0X2VuZXJneV90ZXN0X3ByZWRpY3QubWFwZSoxMDAsIDMpLCAiJSIpKQpsaW5lcyh0cyhiZXN0X2VuZXJneS50ZXN0LnByZWRpY3RbLDFdLCBzdGFydCA9IDE5MjErblRyYWluKzEsIGVuZCA9IDIwMjApLCBjb2wgPSAyKQoKI0R1cmF0aW9uCmJlc3RfZHVyYXRpb24gPSB0cyhhcy5udW1lcmljKGJlc3RfZmVhdHVyZV9xdWFudGlsZXNbLCJkdXJhdGlvbl9tcyJdKSwgc3RhcnQ9MTkyMSwgZW5kPTIwMjApIApiZXN0X2R1cmF0aW9uLnRyYWluID0gc3Vic2V0KGJlc3RfZHVyYXRpb24sIHN0YXJ0ID0gMSwgZW5kID0gblRyYWluKQpiZXN0X2R1cmF0aW9uLnRlc3QgPSBzdWJzZXQoYmVzdF9kdXJhdGlvbiwgc3RhcnQgPSBuVHJhaW4rMSwgZW5kID0gblRyYWluK25UZXN0KQp0cmFpbl9kZiA9IGRhdGEuZnJhbWUoYmVzdCA9IGJlc3RfZHVyYXRpb24udHJhaW4sIG92ZXJhbGwgPSBkdXJhdGlvbi50cmFpbikKCmJlc3QgPSBiZXN0X2R1cmF0aW9uLnRyYWluCm92ZXJhbGwgPSBkdXJhdGlvbi50cmFpbgoKZHVyYXRpb24ubG0gPC0gbG0oYmVzdCB+IG92ZXJhbGwpCnRlc3RfZGYgPC0gZGF0YS5mcmFtZShvdmVyYWxsID0gZHVyYXRpb24udGVzdCkKYmVzdF9kdXJhdGlvbi50ZXN0LnByZWRpY3QgPC0gcHJlZGljdChkdXJhdGlvbi5sbSwgbmV3ZGF0YT10ZXN0X2RmLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCmJlc3RfZHVyYXRpb25fdGVzdC5wcmVkaWN0Lm1hcGUgPC0gbWFwZShiZXN0X2R1cmF0aW9uLnRlc3RbMTpsZW5ndGgoYmVzdF9kdXJhdGlvbi50ZXN0KV0sIGJlc3RfZHVyYXRpb24udGVzdC5wcmVkaWN0WywxXSkKcGxvdChiZXN0X2R1cmF0aW9uLHhsYWI9IlllYXIiLCB5bGFiPSJkdXJhdGlvbiIsIG1haW49cGFzdGUoImR1cmF0aW9uIFBvcHVsYXJpdHkgUHJlZGljdGlvbiIpLCBzdWIgPSBwYXN0ZSgiTUFQRTogIiwgcm91bmQoYmVzdF9kdXJhdGlvbl90ZXN0LnByZWRpY3QubWFwZSoxMDAsIDMpLCAiJSIpKQpsaW5lcyh0cyhiZXN0X2R1cmF0aW9uLnRlc3QucHJlZGljdFssMV0sIHN0YXJ0ID0gMTkyMStuVHJhaW4rMSwgZW5kID0gMjAyMCksIGNvbCA9IDIpCgojQWNjb3VzdGljbmVzcwpiZXN0X2Fjb3VzdGljbmVzcyA9IHRzKGFzLm51bWVyaWMoYmVzdF9mZWF0dXJlX3F1YW50aWxlc1ssImFjb3VzdGljbmVzcyJdKSwgc3RhcnQ9MTkyMSwgZW5kPTIwMjApIApiZXN0X2Fjb3VzdGljbmVzcy50cmFpbiA9IHN1YnNldChiZXN0X2Fjb3VzdGljbmVzcywgc3RhcnQgPSAxLCBlbmQgPSBuVHJhaW4pCmJlc3RfYWNvdXN0aWNuZXNzLnRlc3QgPSBzdWJzZXQoYmVzdF9hY291c3RpY25lc3MsIHN0YXJ0ID0gblRyYWluKzEsIGVuZCA9IG5UcmFpbituVGVzdCkKdHJhaW5fZGYgPSBkYXRhLmZyYW1lKGJlc3QgPSBiZXN0X2Fjb3VzdGljbmVzcy50cmFpbiwgb3ZlcmFsbCA9IGFjb3VzdGljbmVzcy50cmFpbikKCmJlc3QgPSBiZXN0X2Fjb3VzdGljbmVzcy50cmFpbgpvdmVyYWxsID0gYWNvdXN0aWNuZXNzLnRyYWluCgphY291c3RpY25lc3MubG0gPC0gbG0oYmVzdCB+IG92ZXJhbGwpCnRlc3RfZGYgPC0gZGF0YS5mcmFtZShvdmVyYWxsID0gYWNvdXN0aWNuZXNzLnRlc3QpCmJlc3RfYWNvdXN0aWNuZXNzLnRlc3QucHJlZGljdCA8LSBwcmVkaWN0KGFjb3VzdGljbmVzcy5sbSwgbmV3ZGF0YT10ZXN0X2RmLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCmJlc3RfYWNvdXN0aWNuZXNzX3Rlc3QucHJlZGljdC5tYXBlIDwtIG1hcGUoYmVzdF9hY291c3RpY25lc3MudGVzdFsxOmxlbmd0aChiZXN0X2Fjb3VzdGljbmVzcy50ZXN0KV0sIGJlc3RfYWNvdXN0aWNuZXNzLnRlc3QucHJlZGljdFssMV0pCnBsb3QoYmVzdF9hY291c3RpY25lc3MseGxhYj0iWWVhciIsIHlsYWI9ImFjb3VzdGljbmVzcyIsIG1haW49cGFzdGUoImFjb3VzdGljbmVzcyBQb3B1bGFyaXR5IFByZWRpY3Rpb24iKSwgc3ViID0gcGFzdGUoIk1BUEU6ICIsIHJvdW5kKGJlc3RfYWNvdXN0aWNuZXNzX3Rlc3QucHJlZGljdC5tYXBlKjEwMCwgMyksICIlIikpCmxpbmVzKHRzKGJlc3RfYWNvdXN0aWNuZXNzLnRlc3QucHJlZGljdFssMV0sIHN0YXJ0ID0gMTkyMStuVHJhaW4rMSwgZW5kID0gMjAyMCksIGNvbCA9IDIpCgojVmFsZW5jZQpiZXN0X3ZhbGVuY2UgPSB0cyhhcy5udW1lcmljKGJlc3RfZmVhdHVyZV9xdWFudGlsZXNbLCJ2YWxlbmNlIl0pLCBzdGFydD0xOTIxLCBlbmQ9MjAyMCkgCmJlc3RfdmFsZW5jZS50cmFpbiA9IHN1YnNldChiZXN0X3ZhbGVuY2UsIHN0YXJ0ID0gMSwgZW5kID0gblRyYWluKQpiZXN0X3ZhbGVuY2UudGVzdCA9IHN1YnNldChiZXN0X3ZhbGVuY2UsIHN0YXJ0ID0gblRyYWluKzEsIGVuZCA9IG5UcmFpbituVGVzdCkKdHJhaW5fZGYgPSBkYXRhLmZyYW1lKGJlc3QgPSBiZXN0X3ZhbGVuY2UudHJhaW4sIG92ZXJhbGwgPSB2YWxlbmNlLnRyYWluKQoKYmVzdCA9IGJlc3RfdmFsZW5jZS50cmFpbgpvdmVyYWxsID0gdmFsZW5jZS50cmFpbgoKdmFsZW5jZS5sbSA8LSBsbShiZXN0IH4gb3ZlcmFsbCkKdGVzdF9kZiA8LSBkYXRhLmZyYW1lKG92ZXJhbGwgPSB2YWxlbmNlLnRlc3QpCmJlc3RfdmFsZW5jZS50ZXN0LnByZWRpY3QgPC0gcHJlZGljdCh2YWxlbmNlLmxtLCBuZXdkYXRhPXRlc3RfZGYsIGludGVydmFsPSJwcmVkaWN0aW9uIikKYmVzdF92YWxlbmNlX3Rlc3QucHJlZGljdC5tYXBlIDwtIG1hcGUoYmVzdF92YWxlbmNlLnRlc3RbMTpsZW5ndGgoYmVzdF92YWxlbmNlLnRlc3QpXSwgYmVzdF92YWxlbmNlLnRlc3QucHJlZGljdFssMV0pCnBsb3QoYmVzdF92YWxlbmNlLHhsYWI9IlllYXIiLCB5bGFiPSJWYWxlbmNlIiwgbWFpbj1wYXN0ZSgiVmFsZW5jZSBQb3B1bGFyaXR5IFByZWRpY3Rpb24iKSwgc3ViID0gcGFzdGUoIk1BUEU6ICIsIHJvdW5kKGJlc3RfdmFsZW5jZV90ZXN0LnByZWRpY3QubWFwZSoxMDAsIDMpLCAiJSIpKQpsaW5lcyh0cyhiZXN0X3ZhbGVuY2UudGVzdC5wcmVkaWN0WywxXSwgc3RhcnQgPSAxOTIxK25UcmFpbisxLCBlbmQgPSAyMDIwKSwgY29sID0gMikKCiNUZW1wbwpiZXN0X3RlbXBvID0gdHMoYXMubnVtZXJpYyhiZXN0X2ZlYXR1cmVfcXVhbnRpbGVzWywidGVtcG8iXSksIHN0YXJ0PTE5MjEsIGVuZD0yMDIwKSAKYmVzdF90ZW1wby50cmFpbiA9IHN1YnNldChiZXN0X3RlbXBvLCBzdGFydCA9IDEsIGVuZCA9IG5UcmFpbikKYmVzdF90ZW1wby50ZXN0ID0gc3Vic2V0KGJlc3RfdGVtcG8sIHN0YXJ0ID0gblRyYWluKzEsIGVuZCA9IG5UcmFpbituVGVzdCkKdHJhaW5fZGYgPSBkYXRhLmZyYW1lKGJlc3QgPSBiZXN0X3RlbXBvLnRyYWluLCBvdmVyYWxsID0gdGVtcG8udHJhaW4pCgpiZXN0ID0gYmVzdF90ZW1wby50cmFpbgpvdmVyYWxsID0gdGVtcG8udHJhaW4KCnRlbXBvLmxtIDwtIGxtKGJlc3QgfiBvdmVyYWxsKQp0ZXN0X2RmIDwtIGRhdGEuZnJhbWUob3ZlcmFsbCA9IHRlbXBvLnRlc3QpCmJlc3RfdGVtcG8udGVzdC5wcmVkaWN0IDwtIHByZWRpY3QodGVtcG8ubG0sIG5ld2RhdGE9dGVzdF9kZiwgaW50ZXJ2YWw9InByZWRpY3Rpb24iKQpiZXN0X3RlbXBvX3Rlc3QucHJlZGljdC5tYXBlIDwtIG1hcGUoYmVzdF90ZW1wby50ZXN0WzE6bGVuZ3RoKGJlc3RfdGVtcG8udGVzdCldLCBiZXN0X3RlbXBvLnRlc3QucHJlZGljdFssMV0pCnBsb3QoYmVzdF90ZW1wbyx4bGFiPSJZZWFyIiwgeWxhYj0idGVtcG8iLCBtYWluPXBhc3RlKCJ0ZW1wbyBQb3B1bGFyaXR5IFByZWRpY3Rpb24iKSwgc3ViID0gcGFzdGUoIk1BUEU6ICIsIHJvdW5kKGJlc3RfdGVtcG9fdGVzdC5wcmVkaWN0Lm1hcGUqMTAwLCAzKSwgIiUiKSkKbGluZXModHMoYmVzdF90ZW1wby50ZXN0LnByZWRpY3RbLDFdLCBzdGFydCA9IDE5MjErblRyYWluKzEsIGVuZCA9IDIwMjApLCBjb2wgPSAyKQoKI0xpdmVuZXNzCmJlc3RfbGl2ZW5lc3MgPSB0cyhhcy5udW1lcmljKGJlc3RfZmVhdHVyZV9xdWFudGlsZXNbLCJsaXZlbmVzcyJdKSwgc3RhcnQ9MTkyMSwgZW5kPTIwMjApIApiZXN0X2xpdmVuZXNzLnRyYWluID0gc3Vic2V0KGJlc3RfbGl2ZW5lc3MsIHN0YXJ0ID0gMSwgZW5kID0gblRyYWluKQpiZXN0X2xpdmVuZXNzLnRlc3QgPSBzdWJzZXQoYmVzdF9saXZlbmVzcywgc3RhcnQgPSBuVHJhaW4rMSwgZW5kID0gblRyYWluK25UZXN0KQp0cmFpbl9kZiA9IGRhdGEuZnJhbWUoYmVzdCA9IGJlc3RfbGl2ZW5lc3MudHJhaW4sIG92ZXJhbGwgPSBsaXZlbmVzcy50cmFpbikKCmJlc3QgPSBiZXN0X2xpdmVuZXNzLnRyYWluCm92ZXJhbGwgPSBsaXZlbmVzcy50cmFpbgoKbGl2ZW5lc3MubG0gPC0gbG0oYmVzdCB+IG92ZXJhbGwpCnRlc3RfZGYgPC0gZGF0YS5mcmFtZShvdmVyYWxsID0gbGl2ZW5lc3MudGVzdCkKYmVzdF9saXZlbmVzcy50ZXN0LnByZWRpY3QgPC0gcHJlZGljdChsaXZlbmVzcy5sbSwgbmV3ZGF0YT10ZXN0X2RmLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCmJlc3RfbGl2ZW5lc3NfdGVzdC5wcmVkaWN0Lm1hcGUgPC0gbWFwZShiZXN0X2xpdmVuZXNzLnRlc3RbMTpsZW5ndGgoYmVzdF9saXZlbmVzcy50ZXN0KV0sIGJlc3RfbGl2ZW5lc3MudGVzdC5wcmVkaWN0WywxXSkKcGxvdChiZXN0X2xpdmVuZXNzLHhsYWI9IlllYXIiLCB5bGFiPSJsaXZlbmVzcyIsIG1haW49cGFzdGUoImxpdmVuZXNzIFBvcHVsYXJpdHkgUHJlZGljdGlvbiIpLCBzdWIgPSBwYXN0ZSgiTUFQRTogIiwgcm91bmQoYmVzdF9saXZlbmVzc190ZXN0LnByZWRpY3QubWFwZSoxMDAsIDMpLCAiJSIpKQpsaW5lcyh0cyhiZXN0X2xpdmVuZXNzLnRlc3QucHJlZGljdFssMV0sIHN0YXJ0ID0gMTkyMStuVHJhaW4rMSwgZW5kID0gMjAyMCksIGNvbCA9IDIpCgojTG91ZG5lc3MKYmVzdF9sb3VkbmVzcyA9IHRzKGFzLm51bWVyaWMoYmVzdF9mZWF0dXJlX3F1YW50aWxlc1ssImxvdWRuZXNzIl0pLCBzdGFydD0xOTIxLCBlbmQ9MjAyMCkgCmJlc3RfbG91ZG5lc3MudHJhaW4gPSBzdWJzZXQoYmVzdF9sb3VkbmVzcywgc3RhcnQgPSAxLCBlbmQgPSBuVHJhaW4pCmJlc3RfbG91ZG5lc3MudGVzdCA9IHN1YnNldChiZXN0X2xvdWRuZXNzLCBzdGFydCA9IG5UcmFpbisxLCBlbmQgPSBuVHJhaW4rblRlc3QpCnRyYWluX2RmID0gZGF0YS5mcmFtZShiZXN0ID0gYmVzdF9sb3VkbmVzcy50cmFpbiwgb3ZlcmFsbCA9IGxvdWRuZXNzLnRyYWluKQoKYmVzdCA9IGJlc3RfbG91ZG5lc3MudHJhaW4Kb3ZlcmFsbCA9IGxvdWRuZXNzLnRyYWluCgpsb3VkbmVzcy5sbSA8LSBsbShiZXN0IH4gb3ZlcmFsbCkKdGVzdF9kZiA8LSBkYXRhLmZyYW1lKG92ZXJhbGwgPSBsb3VkbmVzcy50ZXN0KQpiZXN0X2xvdWRuZXNzLnRlc3QucHJlZGljdCA8LSBwcmVkaWN0KGxvdWRuZXNzLmxtLCBuZXdkYXRhPXRlc3RfZGYsIGludGVydmFsPSJwcmVkaWN0aW9uIikKYmVzdF9sb3VkbmVzc190ZXN0LnByZWRpY3QubWFwZSA8LSBtYXBlKGJlc3RfbG91ZG5lc3MudGVzdFsxOmxlbmd0aChiZXN0X2xvdWRuZXNzLnRlc3QpXSwgYmVzdF9sb3VkbmVzcy50ZXN0LnByZWRpY3RbLDFdKQpwbG90KGJlc3RfbG91ZG5lc3MseGxhYj0iWWVhciIsIHlsYWI9ImxvdWRuZXNzIiwgbWFpbj1wYXN0ZSgibG91ZG5lc3MgUG9wdWxhcml0eSBQcmVkaWN0aW9uIiksIHN1YiA9IHBhc3RlKCJNQVBFOiAiLCByb3VuZChiZXN0X2xvdWRuZXNzX3Rlc3QucHJlZGljdC5tYXBlKjEwMCwgMyksICIlIikpCmxpbmVzKHRzKGJlc3RfbG91ZG5lc3MudGVzdC5wcmVkaWN0WywxXSwgc3RhcnQgPSAxOTIxK25UcmFpbisxLCBlbmQgPSAyMDIwKSwgY29sID0gMikKYGBgCiMjIyMjIEluc2lnaHRzOgoqKkVuZXJneSoqOiBBIGxvdyBNQVBFIGFyb3VuZCA0JSBpbmRpY2F0ZXMgdGhhdCB3ZSBjYW4gdXNlIHRoZSBvdmVyYWxsIHRyZW5kIHRvIGlkZW50aWZ5IHRoZSBlbmVyZ3kgb2YgcG9wdWxhciBzb25ncwoqKkR1cmF0aW9uKio6IEEgbG93IE1BUEUgYXJvdW5kIDElIGluZGljYXRlcyB0aGF0IHdlIGNhbiB1c2UgdGhlIG92ZXJhbGwgdHJlbmQgdG8gaWRlbnRpZnkgdGhlIGR1cmF0aW9uIG9mIHBvcHVsYXIgc29uZ3MKKipBY291c3RpY25lc3MqKjogQSBoaWdoIE1BUEUgYXJvdW5kIDE2JSBpbmRpY2F0ZXMgdGhhdCB3ZSBjYW4gbm90IHVzZSB0aGUgb3ZlcmFsbCB0cmVuZCB0byBpZGVudGlmeSB0aGUgYWNvdXN0aWNuZXNzIG9mIHBvcHVsYXIgc29uZ3MuIFdlIHdpbGwgbm90IGluY2x1ZGUgcHJlZGljdGlvbnMgZm9yIGFjb3VzdGljbmVzcyBvZiBwb3B1bGFyIHNvbmdzLgoqKlZhbGVuY2UqKjogQSBsb3cgTUFQRSBhcm91bmQgNC42JSBpbmRpY2F0ZXMgdGhhdCB3ZSBjYW4gdXNlIHRoZSBvdmVyYWxsIHRyZW5kIHRvIGlkZW50aWZ5IHRoZSB2YWxlbmNlIG9mIHBvcHVsYXIgc29uZ3MKKipUZW1wbyoqOiBBIGxvdyBNQVBFIGFyb3VuZCAyJSBpbmRpY2F0ZXMgdGhhdCB3ZSBjYW4gdXNlIHRoZSBvdmVyYWxsIHRyZW5kIHRvIGlkZW50aWZ5IHRoZSB0ZW1wbyBvZiBwb3B1bGFyIHNvbmdzCioqTGl2ZW5lc3MqKjogQSBsb3cgTUFQRSBhcm91bmQgNC4zJSBpbmRpY2F0ZXMgdGhhdCB3ZSBjYW4gdXNlIHRoZSBvdmVyYWxsIHRyZW5kIHRvIGlkZW50aWZ5IHRoZSBsaXZlbmVzcyBvZiBwb3B1bGFyIHNvbmdzCioqTG91ZG5lc3MqKjogQSBsb3cgTUFQRSBhcm91bmQgNSUgaW5kaWNhdGVzIHRoYXQgd2UgY2FuIHVzZSB0aGUgb3ZlcmFsbCB0cmVuZCB0byBpZGVudGlmeSB0aGUgbG91ZG5lc3Mgb2YgcG9wdWxhciBzb25ncwoKIyBQcmVkaWN0IGZlYXR1cmVzIGZvciB0aGUgb3ZlcmFsbCBtYXJrZXQgdHJlbmRzIGZvciB0aGUgbmV4dCBmaXZlIHllYXJzLgojIyMgT25seSBpbmNsdWRpbmcgZmVhdHVyZXMgd2hvc2UgdHJhaW5lZCBsaW5lYXIgbW9kZWxzIGhhZCBhIE1BUEUgYmVsb3cgMTAlLCBhcyB0aGVzZSBhcmUgdGhlIGZlYXR1cmVzIHdob3NlIG1vc3QgcG9wdWxhciBtZWRpYW5zIHdpbGwgYmUgbW9zdCByZWxpYWJseSBwcmVkaWN0ZWQgYnkgdGhlaXIgZm9yZWNhc3Qgb3ZlcmFsbCB2YWx1ZXMuCiMjIyBVc2luZyB0aGUgYmVzdCB0aW1lc2VyaWVzIG1ldGhvZCBzZWVuIGZvciBlYWNoIGZlYXR1cmUuCgojIyBFbmVyZ3k6IFNFUyBNb2RlbAoKYGBge3J9CmVuZXJneS5zZXMgPC0gc2VzKGVuZXJneSwgaD1uVGVzdCkKcGxvdChlbmVyZ3kuc2VzLCBtYWluPXBhc3RlKCJFbmVyZ3kiLCAiU0VTIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyhlbmVyZ3kpCmBgYAoKIyMgRHVyYXRpb246IE5OIE1vZGVsCgpgYGB7cn0KZHVyYXRpb24ubm4gPC0gZWxtKGR1cmF0aW9uKQpkdXJhdGlvbi5ubi5wcmVkaWN0IDwtIGZvcmVjYXN0KGR1cmF0aW9uLm5uLCBoPW5UZXN0KQpwbG90KGR1cmF0aW9uLm5uLnByZWRpY3QsIG1haW49cGFzdGUoIkR1cmF0aW9uIiwgIk5OIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpgYGAKCiMjIFRlbXBvOiBTRVMgTW9kZWwgCgpgYGB7cn0KdGVtcG8uc2VzIDwtIHNlcyh0ZW1wbywgaD1uVGVzdCkKcGxvdCh0ZW1wby5zZXMsIG1haW49cGFzdGUoIlRlbXBvIiwgIlNFUyIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXModGVtcG8pCmBgYAoKIyMgVmFsZW5jZTogU0FSSU1BIE1vZGVsCgpgYGB7cn0KdmFsZW5jZS5zYXJpbWEgPC0gYXV0by5hcmltYSh2YWxlbmNlKQp2YWxlbmNlLnNhcmltYS5wcmVkaWN0IDwtIGZvcmVjYXN0KHZhbGVuY2Uuc2FyaW1hLCBoPW5UZXN0KQpwbG90KHZhbGVuY2Uuc2FyaW1hLnByZWRpY3QsIG1haW49cGFzdGUoIlZhbGVuY2UiLCAiU0FSSU1BIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyh2YWxlbmNlKQpgYGAKCiMjIExpdmVuZXNzOiBTQVJJTUEgTW9kZWwKCmBgYHtyfQpsaXZlbmVzcy5zYXJpbWEgPC0gYXV0by5hcmltYShsaXZlbmVzcykKbGl2ZW5lc3Muc2FyaW1hLnByZWRpY3QgPC0gZm9yZWNhc3QobGl2ZW5lc3Muc2FyaW1hLCBoPW5UZXN0KQpwbG90KGxpdmVuZXNzLnNhcmltYS5wcmVkaWN0LCBtYWluPXBhc3RlKCJsaXZlbmVzcyIsICJTQVJJTUEiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKGxpdmVuZXNzKQpgYGAKCiMjIExvdWRuZXNzOiBTRVMgTW9kZWwgCgpgYGB7cn0KbG91ZG5lc3Muc2VzIDwtIHNlcyhsb3VkbmVzcywgaD1uVGVzdCkKcGxvdChsb3VkbmVzcy5zZXMsIG1haW49cGFzdGUoImxvdWRuZXNzIiwgIlNFUyIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXMobG91ZG5lc3MpCmBgYAoKCgoKCiMgTm93IHByZWRpY3RpbmcgdGhlIG1lZGlhbiB2YWx1ZXMgZm9yIHRoZSAqbW9zdCBwb3B1bGFyIHNvbmdzKiBmb3IgdGhpcyBzZXQgb2YgZmVhdHVyZXMuCiMjIyBUaGUgZm9yZWNhc3Qgb3ZlcmFsbCBtZWRpYW4gdmFsdWVzIGFuZCBsaW5lYXIgbW9kZWwgcHJlZGljdGluZyB0aGUgbW9zdCBwb3B1bGFyIG1lZGlhbiB2YWx1ZXMgZm9yIGVhY2ggZmVhdHVyZSBnaXZlbiBvdmVyYWxsIG1lZGlhbiB2YWx1ZSB3aWxsIGJlIHVzZWQgdG8gcHJlZGljdCB0aGUgZnV0dXJlIG1lZGlhbiB2YWx1ZXMgZm9yIHBvcHVsYXIgc29uZ3MgZm9yIGVhY2ggZmVhdHVyZS4KCiMjIEVuZXJneQoKYGBge3J9CmJlc3QgPSBiZXN0X2VuZXJneQpvdmVyYWxsID0gZW5lcmd5CgoKZW5lcmd5LmxtIDwtIGxtKGJlc3QgfiBvdmVyYWxsKQpwcmVkaWN0X2RmIDwtIGRhdGEuZnJhbWUob3ZlcmFsbCA9IGVuZXJneS5zZXMkbWVhbikKZW5lcmd5LnByZWRpY3QgPC0gcHJlZGljdChlbmVyZ3kubG0sIG5ld2RhdGE9cHJlZGljdF9kZiwgaW50ZXJ2YWw9InByZWRpY3Rpb24iKQoKc21hbGxlc3QgPSBtaW4obWluKGJlc3QpLCBtaW4ob3ZlcmFsbCkpCmJpZ2dlc3QgPSBtYXgobWF4KGJlc3QpLCBtYXgob3ZlcmFsbCkpCnBsb3QoZW5lcmd5LCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIiwgeWxpbT1jKHNtYWxsZXN0LCBiaWdnZXN0KSwgbWFpbj0iTW9zdCBQb3B1bGFyIFNvbmcncyBFbmVyZ3kgRmVhdHVyZSBQcmVkaWN0aW9uIiwgc3ViPXBhc3RlKCJQcm9qZWN0ZWQgbWVkaWFuIGZlYXR1cmUgdmFsdWUgZm9yIG1vc3QgcG9wdWxhciBzb25nczoiLCBwYXN0ZShyb3VuZChlbmVyZ3kuc2VzJG1lYW4sIDMpLCBjb2xsYXBzZT0iLCIpICkpCmxpbmVzKGJlc3RfZW5lcmd5LCBjb2wgPSAyKQpsaW5lcyh0cyhlbmVyZ3kucHJlZGljdFssMV0sIHN0YXJ0PTIwMjArMSwgZW5kPTIwMjArblRlc3QpLCBjb2wgPSAzKQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwgbHR5PTEsIGNvbD1jKDEsMiwzKSwgbGVnZW5kPWMoIk92ZXJhbGwiLCAiTW9zdCBQb3B1bGFyIiwgIk1vc3QgUG9wdWxhciBQcm9qZWN0aW9uIikpCmBgYAoKCiMjIER1cmF0aW9uCgpgYGB7cn0KYmVzdCA9IGJlc3RfZHVyYXRpb24Kb3ZlcmFsbCA9IGR1cmF0aW9uCgpkdXJhdGlvbi5sbSA8LSBsbShiZXN0IH4gb3ZlcmFsbCkKcHJlZGljdF9kZiA8LSBkYXRhLmZyYW1lKG92ZXJhbGwgPSBkdXJhdGlvbi5ubi5wcmVkaWN0JG1lYW4pCmR1cmF0aW9uLnByZWRpY3QgPC0gcHJlZGljdChkdXJhdGlvbi5sbSwgbmV3ZGF0YT1wcmVkaWN0X2RmLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCgpzbWFsbGVzdCA9IG1pbihtaW4oYmVzdCksIG1pbihvdmVyYWxsKSkKYmlnZ2VzdCA9IG1heChtYXgoYmVzdCksIG1heChvdmVyYWxsKSkKcGxvdChkdXJhdGlvbiwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIsIHlsaW09YyhzbWFsbGVzdCwgYmlnZ2VzdCksIG1haW49Ik1vc3QgUG9wdWxhciBTb25nJ3MgRHVyYXRpb24gRmVhdHVyZSBQcmVkaWN0aW9uIiwgc3ViPXBhc3RlKCJQcm9qZWN0ZWQgbWVkaWFuIGZlYXR1cmUgdmFsdWUgZm9yIG1vc3QgcG9wdWxhciBzb25nczoiLCBwYXN0ZShyb3VuZChkdXJhdGlvbi5ubi5wcmVkaWN0JG1lYW4pLCBjb2xsYXBzZT0iLCIpICkpCmxpbmVzKGJlc3RfZHVyYXRpb24sIGNvbCA9IDIpCmxpbmVzKHRzKGR1cmF0aW9uLnByZWRpY3RbLDFdLCBzdGFydD0yMDIwKzEsIGVuZD0yMDIwK25UZXN0KSwgY29sID0gMykKbGVnZW5kKCJ0b3ByaWdodCIsIGx0eT0xLCBjb2w9YygxLDIsMyksIGxlZ2VuZD1jKCJPdmVyYWxsIiwgIk1vc3QgUG9wdWxhciIsICJNb3N0IFBvcHVsYXIgUHJvamVjdGlvbiIpKQpgYGAKCgojIyBUZW1wbwoKYGBge3J9CmJlc3QgPSBiZXN0X3RlbXBvCm92ZXJhbGwgPSB0ZW1wbwoKdGVtcG8ubG0gPC0gbG0oYmVzdCB+IG92ZXJhbGwpCnByZWRpY3RfZGYgPC0gZGF0YS5mcmFtZShvdmVyYWxsID0gdGVtcG8uc2VzJG1lYW4pCnRlbXBvLnByZWRpY3QgPC0gcHJlZGljdCh0ZW1wby5sbSwgbmV3ZGF0YT1wcmVkaWN0X2RmLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCgoKc21hbGxlc3QgPSBtaW4obWluKGJlc3QpLCBtaW4ob3ZlcmFsbCkpCmJpZ2dlc3QgPSBtYXgobWF4KGJlc3QpLCBtYXgob3ZlcmFsbCkpCnBsb3QodGVtcG8sIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiLCB5bGltPWMoc21hbGxlc3QsIGJpZ2dlc3QpLCBtYWluPSJNb3N0IFBvcHVsYXIgU29uZydzIFRlbXBvIEZlYXR1cmUgUHJlZGljdGlvbiIsIHN1Yj1wYXN0ZSgiUHJvamVjdGVkIG1lZGlhbiBmZWF0dXJlIHZhbHVlIGZvciBtb3N0IHBvcHVsYXIgc29uZ3M6IiwgcGFzdGUocm91bmQodGVtcG8uc2VzJG1lYW4pLCBjb2xsYXBzZT0iLCIpICkpCmxpbmVzKGJlc3RfdGVtcG8sIGNvbCA9IDIpCmxpbmVzKHRzKHRlbXBvLnByZWRpY3RbLDFdLCBzdGFydD0yMDIwKzEsIGVuZD0yMDIwK25UZXN0KSwgY29sID0gMykKbGVnZW5kKCJib3R0b21yaWdodCIsIGx0eT0xLCBjb2w9YygxLDIsMyksIGxlZ2VuZD1jKCJPdmVyYWxsIiwgIk1vc3QgUG9wdWxhciIsICJNb3N0IFBvcHVsYXIgUHJvamVjdGlvbiIpKQpgYGAKCgojIyBWYWxlbmNlCgpgYGB7cn0KYmVzdCA9IGJlc3RfdmFsZW5jZQpvdmVyYWxsID0gdmFsZW5jZQoKdmFsZW5jZS5sbSA8LSBsbShiZXN0IH4gb3ZlcmFsbCkKcHJlZGljdF9kZiA8LSBkYXRhLmZyYW1lKG92ZXJhbGwgPSB2YWxlbmNlLnNhcmltYS5wcmVkaWN0JG1lYW4pCnZhbGVuY2UucHJlZGljdCA8LSBwcmVkaWN0KHZhbGVuY2UubG0sIG5ld2RhdGE9cHJlZGljdF9kZiwgaW50ZXJ2YWw9InByZWRpY3Rpb24iKQoKCnNtYWxsZXN0ID0gbWluKG1pbihiZXN0KSwgbWluKG92ZXJhbGwpKQpiaWdnZXN0ID0gbWF4KG1heChiZXN0KSwgbWF4KG92ZXJhbGwpKQpwbG90KHZhbGVuY2UsIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiLCB5bGltPWMoc21hbGxlc3QsIGJpZ2dlc3QpLCBtYWluPSJNb3N0IFBvcHVsYXIgU29uZydzIHZhbGVuY2UgRmVhdHVyZSBQcmVkaWN0aW9uIiwgc3ViPXBhc3RlKCJQcm9qZWN0ZWQgbWVkaWFuIGZlYXR1cmUgdmFsdWUgZm9yIG1vc3QgcG9wdWxhciBzb25nczoiLCBwYXN0ZShyb3VuZCh2YWxlbmNlLnNhcmltYS5wcmVkaWN0JG1lYW4sMyksIGNvbGxhcHNlPSIsIikgKSkKbGluZXMoYmVzdF92YWxlbmNlLCBjb2wgPSAyKQpsaW5lcyh0cyh2YWxlbmNlLnByZWRpY3RbLDFdLCBzdGFydD0yMDIwKzEsIGVuZD0yMDIwK25UZXN0KSwgY29sID0gMykKbGVnZW5kKCJib3R0b21yaWdodCIsIGx0eT0xLCBjb2w9YygxLDIsMyksIGxlZ2VuZD1jKCJPdmVyYWxsIiwgIk1vc3QgUG9wdWxhciIsICJNb3N0IFBvcHVsYXIgUHJvamVjdGlvbiIpKQpgYGAKCgojIyBMaXZlbmVzcwoKYGBge3J9CmJlc3QgPSBiZXN0X2xpdmVuZXNzCm92ZXJhbGwgPSBsaXZlbmVzcwoKbGl2ZW5lc3MubG0gPC0gbG0oYmVzdCB+IG92ZXJhbGwpCnByZWRpY3RfZGYgPC0gZGF0YS5mcmFtZShvdmVyYWxsID0gbGl2ZW5lc3Muc2FyaW1hLnByZWRpY3QkbWVhbikKbGl2ZW5lc3MucHJlZGljdCA8LSBwcmVkaWN0KGxpdmVuZXNzLmxtLCBuZXdkYXRhPXByZWRpY3RfZGYsIGludGVydmFsPSJwcmVkaWN0aW9uIikKCnNtYWxsZXN0ID0gbWluKG1pbihiZXN0KSwgbWluKG92ZXJhbGwpKQpiaWdnZXN0ID0gbWF4KG1heChiZXN0KSwgbWF4KG92ZXJhbGwpKQpwbG90KGxpdmVuZXNzLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIiwgeWxpbT1jKHNtYWxsZXN0LCBiaWdnZXN0KSwgbWFpbj0iTW9zdCBQb3B1bGFyIFNvbmcncyBMaXZlbmVzcyBGZWF0dXJlIFByZWRpY3Rpb24iLCBzdWI9cGFzdGUoIlByb2plY3RlZCBtZWRpYW4gZmVhdHVyZSB2YWx1ZSBmb3IgbW9zdCBwb3B1bGFyIHNvbmdzOiIsIHBhc3RlKHJvdW5kKGxpdmVuZXNzLnNhcmltYS5wcmVkaWN0JG1lYW4sMyksIGNvbGxhcHNlPSIsIikgKSkKbGluZXMoYmVzdF9saXZlbmVzcywgY29sID0gMikKbGluZXModHMobGl2ZW5lc3MucHJlZGljdFssMV0sIHN0YXJ0PTIwMjArMSwgZW5kPTIwMjArblRlc3QpLCBjb2wgPSAzKQpsZWdlbmQoInRvcHJpZ2h0IiwgbHR5PTEsIGNvbD1jKDEsMiwzKSwgbGVnZW5kPWMoIk92ZXJhbGwiLCAiTW9zdCBQb3B1bGFyIiwgIk1vc3QgUG9wdWxhciBQcm9qZWN0aW9uIikpCmBgYAoKCgojIyBMb3VkbmVzcwoKYGBge3J9CmJlc3QgPSBiZXN0X2xvdWRuZXNzCm92ZXJhbGwgPSBsb3VkbmVzcwoKbG91ZG5lc3MubG0gPC0gbG0oYmVzdCB+IG92ZXJhbGwpCnByZWRpY3RfZGYgPC0gZGF0YS5mcmFtZShvdmVyYWxsID0gbG91ZG5lc3Muc2VzJG1lYW4pCmxvdWRuZXNzLnByZWRpY3QgPC0gcHJlZGljdChsb3VkbmVzcy5sbSwgbmV3ZGF0YT1wcmVkaWN0X2RmLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCgpzbWFsbGVzdCA9IG1pbihtaW4oYmVzdCksIG1pbihvdmVyYWxsKSkKYmlnZ2VzdCA9IG1heChtYXgoYmVzdCksIG1heChvdmVyYWxsKSkKcGxvdChsb3VkbmVzcywgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIsIHlsaW09YyhzbWFsbGVzdCwgYmlnZ2VzdCksIG1haW49Ik1vc3QgUG9wdWxhciBTb25nJ3MgTG91ZG5lc3MgRmVhdHVyZSBQcmVkaWN0aW9uIiwgc3ViPXBhc3RlKCJQcm9qZWN0ZWQgbWVkaWFuIGZlYXR1cmUgdmFsdWUgZm9yIG1vc3QgcG9wdWxhciBzb25nczoiLCBwYXN0ZShyb3VuZChsb3VkbmVzcy5zZXMkbWVhbiwxKSwgY29sbGFwc2U9IiwiKSApKQpsaW5lcyhiZXN0X2xvdWRuZXNzLCBjb2wgPSAyKQpsaW5lcyh0cyhsb3VkbmVzcy5wcmVkaWN0WywxXSwgc3RhcnQ9MjAyMCsxLCBlbmQ9MjAyMCtuVGVzdCksIGNvbCA9IDMpCmxlZ2VuZCgiYm90dG9tcmlnaHQiLCBsdHk9MSwgY29sPWMoMSwyLDMpLCBsZWdlbmQ9YygiT3ZlcmFsbCIsICJNb3N0IFBvcHVsYXIiLCAiTW9zdCBQb3B1bGFyIFByb2plY3Rpb24iKSkKYGBgCiMjIyBJbXBsaWNhdGlvbnM6ClVzaW5nIHRoZXNlIHZhbHVlcyBhYm92ZSBmb3IgdGhlIHByZWRpY3RlZCBtZWRpYW4gdmFsdWUgZm9yIHRoZSBtb3N0IHBvcHVsYXIgc29uZ3MsIGZvciBlYWNoIGZlYXR1cmUsIG11c2ljIGN1cmF0b3JzIGFuZCByZWNvcmQgbGFiZWxzIGNhbiBwcmVkaWN0IHdoaWNoICJzb3VuZHMiIHdpbGwgYmUgbW9zdCBwb3B1bGFyIGluIHRoZSBmdXR1cmUuIFRoaXMgaW5mb3JtYXRpb24gY2FuIHRoZW4gYmUgdXNlZCBmb3IgZnV0dXJlIGRlY2lzaW9uIG1ha2luZyB3aXRoIHJlc3BlY3QgdG8gd2hpY2ggYXJ0aXN0cyB0byBzaWduIGFuZCB3aGljaCBzb3VuZHMgdG8gcHVzaC4=